strc 1.0.0-a → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/index.d.ts +11 -0
  2. package/index.js +288 -34
  3. package/package.json +1 -1
package/index.d.ts CHANGED
@@ -24,6 +24,17 @@ SOFTWARE.
24
24
 
25
25
  */
26
26
 
27
+ /*
28
+ _______________
29
+ __ / / __/ __/ ___/
30
+ / // /\ \_\ \/ /__
31
+ \___/___/___/\___/
32
+
33
+ JavaScript String Compressor
34
+ https://jssc.js.org/
35
+
36
+ */
37
+
27
38
  /**
28
39
  * JavaScript String Compressor - compress function
29
40
  * @param str - Input string to compress
package/index.js CHANGED
@@ -24,6 +24,17 @@ SOFTWARE.
24
24
 
25
25
  */
26
26
 
27
+ /*
28
+ _______________
29
+ __ / / __/ __/ ___/
30
+ / // /\ \_\ \/ /__
31
+ \___/___/___/\___/
32
+
33
+ JavaScript String Compressor
34
+ https://jssc.js.org/
35
+
36
+ */
37
+
27
38
  (function (root, factory) {
28
39
  if (typeof define === 'function' && define.amd) {
29
40
  define([], factory); /* amd */
@@ -51,7 +62,7 @@ SOFTWARE.
51
62
  maxCharCode = Math.max(maxCharCode, code);
52
63
  min = Math.min(min, code.toString().length);
53
64
  });
54
- return {max, output, maxCharCode, min}
65
+ return {max, output, maxCharCode, min};
55
66
  }
56
67
 
57
68
  function codesString(cds) {
@@ -406,7 +417,7 @@ SOFTWARE.
406
417
  function cryptCharCode(
407
418
  code, get = false,
408
419
  repeatBefore = false, repeatAfter = false,
409
- beginId = -1,
420
+ beginId = -1, code2 = 0, sequences = false
410
421
  ) {
411
422
  if (get) {
412
423
  const codeBin = decToBin(code, 16);
@@ -418,12 +429,173 @@ SOFTWARE.
418
429
  repeatBefore: codeSet[0] === '1',
419
430
  repeatAfter: codeSet[1] === '1',
420
431
  beginId: codeSet[2] === '1' ? begid : -1,
432
+ code2: binToDec(codeBin.slice(0,4)),
433
+ sequences: codeBin.slice(4,5) === '1',
434
+ code3: codeSet[2] === '0' ? begid : -1, /* currently unused */
435
+ bin: codeBin,
421
436
  }
422
437
  } else {
423
- const codeBin = decToBin(code, 5);
424
- const beginBin = beginId >= 0 ? decToBin(beginId, 3) : '';
425
- return binToDec(String(beginBin) + String(repeatBefore ? '1' : '0') + String(repeatAfter ? '1' : '0') + String(beginId >= 0 ? '1' : '0') + String(codeBin));
438
+ const sixteenBits = /* 16-bit Data/Header character */
439
+
440
+ decToBin(code2, 4) + /* Bits 0-3 : code2 */
441
+ (sequences ? '1' : '0') + /* Bit 4 : sequences? */
442
+ (beginId >= 0 ? decToBin(beginId, 3) : '000') + /* Bits 5-7 : beginID | code3 */
443
+ (repeatBefore ? '1' : '0') + /* Bit 8 : input RLE? */
444
+ (repeatAfter ? '1' : '0') + /* Bit 9 : output RLE? */
445
+ (beginId >= 0 ? '1' : '0') + /* Bit 10 : beginID? */
446
+ decToBin(code, 5); /* Bits 11-15 : code1 */
447
+
448
+ return binToDec(sixteenBits);
449
+ }
450
+ }
451
+
452
+ const SEQUENCE_MARKER = '\uDBFF'; /* Private Use Area */
453
+
454
+ function findSequences(str, minLength = 2, minCount = 3) {
455
+ const repeats = [];
456
+ const n = str.length;
457
+
458
+ for (let i = 0; i < n - minLength * minCount + 1; i++) {
459
+ for (let len = 2; len <= Math.min(30, Math.floor((n - i) / minCount)); len++) {
460
+ const pattern = str.substr(i, len);
461
+ if (pattern.includes(SEQUENCE_MARKER)) continue;
462
+
463
+ let count = 1;
464
+ for (let j = i + len; j <= n - len; j += len) {
465
+ if (str.substr(j, len) === pattern) {
466
+ count++;
467
+ } else {
468
+ break;
469
+ }
470
+ }
471
+
472
+ if (count >= minCount) {
473
+ const end = i + len * count;
474
+ let overlaps = false;
475
+ for (const r of repeats) {
476
+ if (i < r.end && end > r.start) {
477
+ overlaps = true;
478
+ break;
479
+ }
480
+ }
481
+
482
+ if (!overlaps) {
483
+ repeats.push({
484
+ start: i,
485
+ end: end,
486
+ length: len,
487
+ pattern: pattern,
488
+ count: count,
489
+ saved: (len * count) - (len + 3)
490
+ });
491
+ i = end - 1;
492
+ break;
493
+ }
494
+ }
495
+ }
426
496
  }
497
+
498
+ return repeats.sort((a, b) => b.saved - a.saved);
499
+ }
500
+
501
+ function compressSequences(str) {
502
+ if (str.length < 20) return {compressed: str, sequences: false};
503
+
504
+ if (str.includes(SEQUENCE_MARKER)) {
505
+ return {compressed: str, sequences: false};
506
+ }
507
+
508
+ const repeats = findSequences(str, 2, 3);
509
+ if (repeats.length === 0) return {compressed: str, sequences: false};
510
+
511
+ const selected = [];
512
+ let covered = new Array(str.length).fill(false);
513
+
514
+ for (const repeat of repeats) {
515
+ let canUse = true;
516
+ for (let i = repeat.start; i < repeat.end; i++) {
517
+ if (covered[i]) {
518
+ canUse = false;
519
+ break;
520
+ }
521
+ }
522
+
523
+ if (canUse && repeat.saved > 0) {
524
+ selected.push(repeat);
525
+ for (let i = repeat.start; i < repeat.end; i++) {
526
+ covered[i] = true;
527
+ }
528
+ }
529
+ }
530
+
531
+ if (selected.length === 0) return {compressed: str, sequences: false};
532
+
533
+ let result = '';
534
+ let pos = 0;
535
+
536
+ for (const repeat of selected) {
537
+ if (pos < repeat.start) {
538
+ result += str.slice(pos, repeat.start);
539
+ }
540
+
541
+ /* sequence encoding: [marker][length][count][pattern] */
542
+ const lengthChar = String.fromCharCode(Math.min(repeat.length, 30) + 32);
543
+ const countChar = String.fromCharCode(Math.min(repeat.count, 65535) + 32);
544
+ result += SEQUENCE_MARKER + lengthChar + countChar + repeat.pattern;
545
+
546
+ pos = repeat.end;
547
+ }
548
+
549
+ if (pos < str.length) {
550
+ result += str.slice(pos);
551
+ }
552
+
553
+ /* check if it's worth it */
554
+ if (result.length < str.length * 0.9) { /* at least 10% */
555
+ return {compressed: result, sequences: true};
556
+ }
557
+
558
+ return {compressed: str, sequences: false};
559
+ }
560
+
561
+ function decompressSequences(str) {
562
+ let result = '';
563
+ let i = 0;
564
+
565
+ while (i < str.length) {
566
+ if (str.charCodeAt(i) === 0xDBFF) {
567
+ i++;
568
+
569
+ if (i + 2 >= str.length) {
570
+ result += SEQUENCE_MARKER;
571
+ continue;
572
+ }
573
+
574
+ const length = str.charCodeAt(i) - 32;
575
+ const count = str.charCodeAt(i + 1) - 32;
576
+ i += 2;
577
+
578
+ if (i + length > str.length) {
579
+ result += SEQUENCE_MARKER +
580
+ String.fromCharCode(length + 32) +
581
+ String.fromCharCode(count + 32) +
582
+ str.slice(i);
583
+ break;
584
+ }
585
+
586
+ const pattern = str.substr(i, length);
587
+ i += length;
588
+
589
+ for (let j = 0; j < count; j++) {
590
+ result += pattern;
591
+ }
592
+ } else {
593
+ result += str.charAt(i);
594
+ i++;
595
+ }
596
+ }
597
+
598
+ return result;
427
599
  }
428
600
 
429
601
  /**
@@ -455,18 +627,27 @@ SOFTWARE.
455
627
  const strdata = stringCodes(str);
456
628
  const ascii = strdata.maxCharCode < 256;
457
629
  let repeatAfter = false;
458
- function checkOutput(out) {
459
- if (d(out)) {
630
+ let sequences = false;
631
+
632
+ function processCompression(output) {
633
+ const hasDigits = /\d/.test(output);
634
+ if (!hasDigits) {
460
635
  repeatAfter = true;
636
+ output = repeatChars(output);
461
637
  }
462
- }
463
- function processOutput(out) {
464
- if (repeatAfter) {
465
- return repeatChars(out);
466
- } else {
467
- return out;
638
+
639
+ if (true) {
640
+ const compressed = compressSequences(output);
641
+ if (compressed.sequences) {
642
+ sequences = true;
643
+ return compressed.compressed;
644
+ }
468
645
  }
646
+
647
+ return output;
469
648
  }
649
+
650
+ const UniqueCodes = [...new Set(strdata.output)];
470
651
  if (/^\d+$/.test(str)) { /* Numbers */
471
652
  /* Up to 8:1 compression ratio */
472
653
  const convertNums = {
@@ -500,10 +681,10 @@ SOFTWARE.
500
681
  for (const character of chunkArray(binOut, 4)) {
501
682
  output += String.fromCharCode(binToDec(binPadStart(character.join(''))));
502
683
  }
503
- checkOutput(output);
504
- return charCode(cryptCharCode(3, false, false, repeatAfter, -1))+processOutput(output);
684
+ output = processCompression(output);
685
+ return charCode(cryptCharCode(3, false, false, repeatAfter, -1, 0, sequences)) + output;
686
+
505
687
  } else if (strdata.max === 2 && strdata.min === 2) {
506
- /* Up to 3:1 compression ratio */
507
688
  let chars = strdata.output;
508
689
  let output = '';
509
690
  function addChar(codee) {
@@ -536,8 +717,9 @@ SOFTWARE.
536
717
  }
537
718
  }
538
719
  }
539
- checkOutput(output);
540
- return charCode(cryptCharCode(1, false, repeatBefore, repeatAfter, beginId))+processOutput(output);
720
+ output = processCompression(output);
721
+ return charCode(cryptCharCode(1, false, repeatBefore, repeatAfter, beginId, 0, sequences)) + output;
722
+
541
723
  } else if (ascii) {
542
724
  /* Up to 2:1 compression ratio */
543
725
  /*
@@ -567,21 +749,24 @@ SOFTWARE.
567
749
  for (const char of twoChars) {
568
750
  output += String.fromCharCode(binToDec(char));
569
751
  }
570
- checkOutput(output);
571
- return charCode(cryptCharCode(2, false, repeatBefore, repeatAfter, beginId))+processOutput(output)
752
+ output = processCompression(output);
753
+ return charCode(cryptCharCode(2, false, repeatBefore, repeatAfter, beginId, 0, sequences)) + output;
754
+
572
755
  } else {
573
756
  const characterEncodings = new _JSSC.use();
574
757
  const stringArray = str.split('');
575
758
  let useCharacterEncoding;
576
759
  let charEncodingID = NaN;
760
+
577
761
  for (const [characterEncodingName, characterEncoding] of Object.entries(characterEncodings)) {
578
762
  const table = characterEncoding();
579
- table.length= 256;
580
- const arrayy= Array.from(table);
763
+ table.length = 256;
764
+ const arrayy = Array.from(table);
581
765
  let usethisone = true;
582
766
  for (const character of stringArray) {
583
767
  if (!arrayy.includes(character)) {
584
768
  usethisone = false;
769
+ break;
585
770
  }
586
771
  }
587
772
  if (usethisone) {
@@ -590,6 +775,7 @@ SOFTWARE.
590
775
  break;
591
776
  }
592
777
  }
778
+
593
779
  if (useCharacterEncoding) {
594
780
  const reverseCharacterEncoding = {};
595
781
  for (const [charCode, character] of Object.entries(useCharacterEncoding)) {
@@ -608,13 +794,47 @@ SOFTWARE.
608
794
  outputStr += String.fromCharCode(binToDec(characterCode))
609
795
  }
610
796
  /* Up to 2:1 compression ratio */
611
- checkOutput(outputStr);
612
- return charCode(cryptCharCode(charEncodingID + 5, false, repeatBefore, repeatAfter, beginId))+processOutput(outputStr);
797
+ outputStr = processCompression(outputStr);
798
+ return charCode(cryptCharCode(charEncodingID + 5, false, repeatBefore, repeatAfter, beginId, 0, sequences)) + outputStr;
799
+
800
+ } else if (UniqueCodes.length < 16) {
801
+ /* Up to 4:1 compression ratio */
802
+ const chars = UniqueCodes;
803
+ let output = '';
804
+ for (const char of chars) {
805
+ output += String.fromCharCode(char);
806
+ }
807
+
808
+ let char_ = [];
809
+ for (const charCode of strdata.output) {
810
+ const index = UniqueCodes.indexOf(charCode);
811
+
812
+ if (char_.length === 4) {
813
+ let outchar = '';
814
+ for (const index of char_) {
815
+ outchar += decToBin(index, 4);
816
+ }
817
+ output += String.fromCharCode(binToDec(outchar));
818
+ char_ = [];
819
+ }
820
+ char_.push(index);
821
+ }
822
+
823
+ if (char_.length > 0) {
824
+ let outchar = '';
825
+ for (const index of char_) {
826
+ outchar += decToBin(index, 4);
827
+ }
828
+ output += String.fromCharCode(binToDec(outchar.padStart(16, '1')));
829
+ }
830
+
831
+ output = processCompression(output);
832
+ return charCode(cryptCharCode(4, false, repeatBefore, repeatAfter, beginId, UniqueCodes.length, sequences)) + output;
613
833
  }
614
834
 
615
835
  /* 1:1 compression ratio (no compression) */
616
- checkOutput(str);
617
- return charCode(cryptCharCode(0, false, repeatBefore, repeatAfter, beginId))+processOutput(str);
836
+ let output = processCompression(str);
837
+ return charCode(cryptCharCode(0, false, repeatBefore, repeatAfter, beginId, 0, sequences)) + output;
618
838
  }
619
839
  }
620
840
 
@@ -654,29 +874,43 @@ SOFTWARE.
654
874
  */
655
875
  function decompress(str) {
656
876
  if (typeof str != 'string') throw new Error('Invalid input.');
657
- const strcodes= cryptCharCode(str.charCodeAt(0) - 32, true);
877
+ const strcodes = cryptCharCode(str.charCodeAt(0) - 32, true);
658
878
  const strcode = strcodes.code;
879
+
659
880
  function repeatChars(txt) {
660
881
  return txt.replace(/(\D)(\d+)/g, (_, g1, g2) => g1.repeat(g2));
661
882
  }
662
- const realstr = strcodes.repeatAfter ? repeatChars(str.slice(1)) : str.slice(1);
883
+
884
+ /* sequences */
885
+ let realstr = str.slice(1);
886
+ if (strcodes.sequences) {
887
+ realstr = decompressSequences(realstr);
888
+ }
889
+
890
+ /* RLE */
891
+ if (strcodes.repeatAfter) {
892
+ realstr = repeatChars(realstr);
893
+ }
894
+
663
895
  function begin(out) {
664
896
  if (strcodes.beginId >= 0) {
665
897
  return _JSSC._begin[strcodes.beginId] + out;
666
- } else return out
898
+ } else return out;
667
899
  }
900
+
668
901
  function processOutput(out) {
669
902
  if (strcodes.repeatBefore) {
670
903
  return repeatChars(begin(out));
671
904
  } else return begin(out);
672
905
  }
906
+
673
907
  let output = '';
674
908
  switch (strcode) {
675
909
  case 0:
676
910
  return processOutput(realstr);
677
911
  case 1:
678
912
  function addChar(cde) {
679
- output += String.fromCharCode(cde)
913
+ output += String.fromCharCode(cde);
680
914
  }
681
915
  for (const char of realstr.split('')) {
682
916
  const charcde = String(char.charCodeAt(0));
@@ -717,14 +951,34 @@ SOFTWARE.
717
951
  }
718
952
  }
719
953
  return processOutput(output);
954
+ case 4:
955
+ const chars = [];
956
+ for (const char of realstr.slice(0, strcodes.code2).split('')) {
957
+ chars.push(char);
958
+ }
959
+ for (const char of realstr.slice(strcodes.code2).split('')) {
960
+ const binCodes = stringChunks(decToBin(char.charCodeAt(0), 16), 4);
961
+ for (const binCode of binCodes) {
962
+ if (binCode != '1111') {
963
+ const numm = binToDec(binCode);
964
+ output += chars[numm];
965
+ }
966
+ }
967
+ }
968
+ return processOutput(output);
720
969
  default:
721
- const err = () => {throw new Error('')};
722
- return processOutput(characterEncodings(strcode, realstr) || err());
970
+ const decoded = characterEncodings(strcode, realstr);
971
+ if (decoded) {
972
+ return processOutput(decoded);
973
+ }
974
+ throw new Error('Invalid compressed string');
723
975
  }
724
976
  }
725
977
 
726
- return {compress, decompress,
727
- get[Symbol.toStringTag]() {
978
+ return {
979
+ compress,
980
+ decompress,
981
+ get [Symbol.toStringTag]() {
728
982
  return 'JSSC';
729
983
  }
730
984
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "strc",
3
- "version": "1.0.0a",
3
+ "version": "1.1.0",
4
4
  "description": "JavaScript String Compressor",
5
5
  "main": "index.js",
6
6
  "scripts": {