├── README.md └── LZString.cs /README.md: -------------------------------------------------------------------------------- 1 | # lz-string-csharp 2 | C# Implementation of the [lz-string javascript library](http://pieroxy.net/blog/pages/lz-string/index.html) 3 | 4 | After the other [lz-string implementation](https://github.com/jawa-the-hutt/lz-string-csharp) returned different results in compressToBase64, I decided to reimplement the newest version of the lz-string library. This isn't optimized or extensively tested, but it seems to work just fine. 5 | -------------------------------------------------------------------------------- /LZString.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace LZString 6 | { 7 | public class LZString 8 | { 9 | static string keyStrBase64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; 10 | static string keyStrUriSafe = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+-$"; 11 | static Dictionary> baseReverseDic = new Dictionary>(); 12 | private delegate char GetCharFromInt(int a); 13 | private static GetCharFromInt f = (a) => Convert.ToChar(a); 14 | private delegate int GetNextValue(int index); 15 | 16 | private static int getBaseValue(string alphabet, char character) 17 | { 18 | if(!baseReverseDic.ContainsKey(alphabet)) 19 | { 20 | baseReverseDic[alphabet] = new Dictionary(); 21 | for (int i = 0; i < alphabet.Length; i++) 22 | { 23 | baseReverseDic[alphabet][alphabet[i]] = i; 24 | } 25 | } 26 | return baseReverseDic[alphabet][character]; 27 | } 28 | 29 | public static string compressToBase64(string input) 30 | { 31 | if (input == null) return ""; 32 | string res = _compress(input, 6, (a) => keyStrBase64[a]); 33 | switch (res.Length%4) 34 | { 35 | case 0: return res; 36 | case 1: return res + "==="; 37 | case 2: return res + "=="; 38 | case 3: return res + "="; 39 | } 40 | return null; 41 | } 42 | 43 | public static string decompressFromBase64(string input) 44 | { 45 | if (input == null) return ""; 46 | if (input == "") return null; 47 | return _decompress(input.Length, 32, (index) => getBaseValue(keyStrBase64, input[index])); 48 | } 49 | 50 | public static string compressToUTF16(string input) 51 | { 52 | if (input == null) return ""; 53 | return _compress(input, 15, (a) => f(a + 32)) + " "; 54 | } 55 | 56 | public static string decompressFromUTF16(string compressed) 57 | { 58 | if (compressed == null) return ""; 59 | if (compressed == "") return null; 60 | return _decompress(compressed.Length, 16384, index => Convert.ToInt32(compressed[index]) - 32); 61 | } 62 | 63 | public static byte[] compressToUint8Array(string uncompressed) 64 | { 65 | string compressed = compress(uncompressed); 66 | byte[] buf = new byte[compressed.Length * 2]; 67 | 68 | for (int i = 0, TotalLen = compressed.Length; i < TotalLen; i++) 69 | { 70 | int current_value = Convert.ToInt32(compressed[i]); 71 | buf[i * 2] = (byte)(((uint)current_value) >> 8); 72 | buf[i * 2 + 1] = (byte)(current_value % 256); 73 | } 74 | return buf; 75 | } 76 | 77 | public static string decompressFromUint8Array(byte[] compressed) 78 | { 79 | if (compressed == null) return ""; 80 | else 81 | { 82 | int[] buf = new int[compressed.Length / 2]; 83 | for (int i = 0, TotalLen = buf.Length; i < TotalLen; i++) 84 | { 85 | buf[i] = ((int)compressed[i * 2]) * 256 + ((int)compressed[i * 2 + 1]); 86 | } 87 | char[] result = new char[buf.Length]; 88 | for (int i = 0; i < buf.Length; i++) 89 | { 90 | result[i] = f(buf[i]); 91 | } 92 | return decompress(new string(result)); 93 | } 94 | } 95 | 96 | public static string compressToEncodedURIComponent(string input) 97 | { 98 | if (input == null) return ""; 99 | return _compress(input, 6, (a) => keyStrUriSafe[a]); 100 | } 101 | 102 | public static string decompressFromEncodedURIComponent(string input) 103 | { 104 | if (input == null) return ""; 105 | if (input == "") return null; 106 | input = input.Replace(' ', '+'); 107 | return _decompress(input.Length, 32, (index) => getBaseValue(keyStrUriSafe, input[index])); 108 | } 109 | 110 | public static string compress(string uncompressed) 111 | { 112 | return _compress(uncompressed, 16, f); 113 | } 114 | 115 | private static string _compress(string uncompressed, int bitsPerChar, GetCharFromInt getCharFromInt) 116 | { 117 | if (uncompressed == null) return ""; 118 | int i, value, ii, context_enlargeIn = 2, context_dictSize = 3, context_numBits = 2, context_data_val = 0, context_data_position = 0; 119 | Dictionary context_dictionaryToCreate = new Dictionary(); 120 | Dictionary context_dictionary = new Dictionary(); 121 | StringBuilder context_data = new StringBuilder(); 122 | string context_c =""; 123 | string context_wc = "", context_w = ""; 124 | 125 | for(ii=0;ii> 1; 173 | } 174 | } 175 | else 176 | { 177 | value = 1; 178 | for(i=0;i> 1; 208 | } 209 | } 210 | context_enlargeIn--; 211 | if(context_enlargeIn==0) 212 | { 213 | context_enlargeIn = (int)Math.Pow(2, context_numBits); 214 | context_numBits++; 215 | } 216 | context_dictionaryToCreate.Remove(context_w); 217 | } 218 | else 219 | { 220 | value = context_dictionary[context_w]; 221 | for(i=0;i> 1; 235 | } 236 | } 237 | context_enlargeIn--; 238 | if(context_enlargeIn==0) 239 | { 240 | context_enlargeIn = (int)Math.Pow(2, context_numBits); 241 | context_numBits++; 242 | } 243 | //Add wc to the dictionary 244 | context_dictionary[context_wc] = context_dictSize++; 245 | context_w = context_c; 246 | } 247 | } 248 | //Output the code for w 249 | if(context_w!="") 250 | { 251 | if(context_dictionaryToCreate.ContainsKey(context_w)) 252 | { 253 | if(Convert.ToInt32(context_w[0])<256) 254 | { 255 | for(i=0;i> 1; 284 | } 285 | } 286 | else 287 | { 288 | value = 1; 289 | for(i=0;i> 1; 319 | } 320 | } 321 | context_enlargeIn--; 322 | if(context_enlargeIn==0) 323 | { 324 | context_enlargeIn = (int)Math.Pow(2, context_numBits); 325 | context_numBits++; 326 | } 327 | context_dictionaryToCreate.Remove(context_w); 328 | } 329 | else 330 | { 331 | value = context_dictionary[context_w]; 332 | for(i=0;i> 1; 346 | } 347 | } 348 | context_enlargeIn--; 349 | if(context_enlargeIn==0) 350 | { 351 | context_enlargeIn = (int)Math.Pow(2, context_numBits); 352 | context_numBits++; 353 | } 354 | } 355 | //Mark the end of the stream 356 | value = 2; 357 | for(i=0;i> 1; 371 | } 372 | 373 | //Flush the last char 374 | while(true) 375 | { 376 | context_data_val = (context_data_val << 1); 377 | if (context_data_position == bitsPerChar - 1) 378 | { 379 | context_data.Append(getCharFromInt(context_data_val)); 380 | break; 381 | } 382 | else context_data_position++; 383 | } 384 | return context_data.ToString(); 385 | } 386 | 387 | public static string decompress(string compressed) 388 | { 389 | if (compressed == null) return ""; 390 | if (compressed == "") return null; 391 | return _decompress(compressed.Length, 32768, (index) => Convert.ToInt32(compressed[index])); 392 | } 393 | 394 | 395 | private struct dataStruct 396 | { 397 | public int val, position, index; 398 | } 399 | private static string _decompress(int length, int resetValue, GetNextValue getNextValue) 400 | { 401 | Dictionary dictionary = new Dictionary(); 402 | int next, enlargeIn = 4, dictSize = 4, numBits = 3, i, bits, resb, maxpower, power; 403 | int c = 0; 404 | string entry = "", w; 405 | StringBuilder result = new StringBuilder(); 406 | var data = new dataStruct(){ val = getNextValue(0), position = resetValue, index = 1 }; 407 | 408 | for (i = 0; i < 3; i++) 409 | { 410 | dictionary[i] = Convert.ToChar(i).ToString(); 411 | } 412 | 413 | bits = 0; 414 | maxpower = (int)Math.Pow(2, 2); 415 | power = 1; 416 | while(power!=maxpower) 417 | { 418 | resb = data.val & data.position; 419 | data.position >>= 1; 420 | if(data.position == 0) 421 | { 422 | data.position = resetValue; 423 | data.val = getNextValue(data.index++); 424 | } 425 | bits |= (resb > 0 ? 1 : 0) * power; 426 | power <<= 1; 427 | } 428 | 429 | switch(next= bits) 430 | { 431 | case 0: 432 | bits = 0; 433 | maxpower = (int)Math.Pow(2, 8); 434 | power = 1; 435 | while (power != maxpower) 436 | { 437 | resb = data.val & data.position; 438 | data.position >>= 1; 439 | if (data.position == 0) 440 | { 441 | data.position = resetValue; 442 | data.val = getNextValue(data.index++); 443 | } 444 | bits |= (resb > 0 ? 1 : 0) * power; 445 | power <<= 1; 446 | } 447 | c = Convert.ToInt32(f(bits)); 448 | break; 449 | case 1: 450 | bits = 0; 451 | maxpower = (int)Math.Pow(2, 16); 452 | power = 1; 453 | while(power!=maxpower) 454 | { 455 | resb = data.val & data.position; 456 | data.position >>= 1; 457 | if(data.position==0) 458 | { 459 | data.position = resetValue; 460 | data.val = getNextValue(data.index++); 461 | } 462 | bits |= (resb > 0 ? 1 : 0) * power; 463 | power <<= 1; 464 | } 465 | c = Convert.ToInt32(f(bits)); 466 | break; 467 | case 2: 468 | return ""; 469 | } 470 | dictionary[3] = Convert.ToChar(c).ToString(); 471 | w = Convert.ToChar(c).ToString(); 472 | result.Append(Convert.ToChar(c)); 473 | while (true) 474 | { 475 | if (data.index > length) 476 | { 477 | return ""; 478 | } 479 | 480 | bits = 0; 481 | maxpower = (int)Math.Pow(2, numBits); 482 | power = 1; 483 | while (power != maxpower) 484 | { 485 | resb = data.val & data.position; 486 | data.position >>= 1; 487 | if (data.position == 0) 488 | { 489 | data.position = resetValue; 490 | data.val = getNextValue(data.index++); 491 | } 492 | bits |= (resb > 0 ? 1 : 0) * power; 493 | power <<= 1; 494 | } 495 | 496 | switch (c = bits) 497 | { 498 | case 0: 499 | bits = 0; 500 | maxpower = (int)Math.Pow(2, 8); 501 | power = 1; 502 | while(power!=maxpower) 503 | { 504 | resb = data.val & data.position; 505 | data.position >>= 1; 506 | if (data.position == 0) 507 | { 508 | data.position = resetValue; 509 | data.val = getNextValue(data.index++); 510 | } 511 | bits |= (resb > 0 ? 1 : 0) * power; 512 | power <<= 1; 513 | } 514 | 515 | dictionary[dictSize++] = f(bits).ToString(); 516 | c = dictSize - 1; 517 | enlargeIn--; 518 | break; 519 | case 1: 520 | bits = 0; 521 | maxpower = (int)Math.Pow(2, 16); 522 | power = 1; 523 | while(power!=maxpower) 524 | { 525 | resb = data.val & data.position; 526 | data.position >>= 1; 527 | if (data.position == 0) 528 | { 529 | data.position = resetValue; 530 | data.val = getNextValue(data.index++); 531 | } 532 | bits |= (resb > 0 ? 1 : 0) * power; 533 | power <<= 1; 534 | } 535 | dictionary[dictSize++] = f(bits).ToString(); 536 | c = dictSize - 1; 537 | enlargeIn--; 538 | break; 539 | case 2: 540 | return result.ToString(); 541 | } 542 | 543 | if(enlargeIn==0) 544 | { 545 | enlargeIn = (int)Math.Pow(2, numBits); 546 | numBits++; 547 | } 548 | 549 | if(dictionary.ContainsKey(c)) 550 | { 551 | entry = dictionary[c]; 552 | } 553 | else 554 | { 555 | if(c==dictSize) 556 | { 557 | entry = w + w[0].ToString(); 558 | } 559 | else 560 | { 561 | return null; 562 | } 563 | } 564 | result.Append(entry); 565 | 566 | //Add w+entry[0] to the dictionary. 567 | dictionary[dictSize++] = w + entry[0].ToString(); 568 | enlargeIn--; 569 | w = entry; 570 | if(enlargeIn ==0) 571 | { 572 | enlargeIn = (int)Math.Pow(2, numBits); 573 | numBits++; 574 | } 575 | } 576 | } 577 | } 578 | } 579 | --------------------------------------------------------------------------------