strc 1.1.0 → 2.0.0-test
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.
- package/LICENSE +1 -1
- package/README.md +92 -22
- package/index.d.ts +57 -6
- package/index.js +794 -160
- package/index.min.js +38 -0
- package/package.json +29 -8
- package/test/index.js +97 -0
package/index.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
MIT License
|
|
4
4
|
|
|
5
|
-
Copyright (c) 2025 JustDeveloper <https://justdeveloper.is-a.dev/>
|
|
5
|
+
Copyright (c) 2025-2026 JustDeveloper <https://justdeveloper.is-a.dev/>
|
|
6
6
|
|
|
7
7
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
8
8
|
of this software and associated documentation files (the "Software"), to deal
|
|
@@ -37,15 +37,14 @@ SOFTWARE.
|
|
|
37
37
|
|
|
38
38
|
(function (root, factory) {
|
|
39
39
|
if (typeof define === 'function' && define.amd) {
|
|
40
|
-
define([], factory);
|
|
40
|
+
define(['justc'], factory); /* amd */
|
|
41
41
|
} else if (typeof module === 'object' && module.exports) {
|
|
42
|
-
module.exports = factory(); /* node */
|
|
42
|
+
module.exports = factory(require('justc')); /* node */
|
|
43
43
|
} else {
|
|
44
|
-
root.JSSC = factory();
|
|
44
|
+
root.JSSC = factory(root.JUSTC); /* browsers */
|
|
45
45
|
Object.freeze(root.JSSC);
|
|
46
46
|
}
|
|
47
|
-
}(typeof self !== 'undefined' ? self : this, function () {
|
|
48
|
-
|
|
47
|
+
}(typeof self !== 'undefined' ? self : this, function (JUSTC) {
|
|
49
48
|
if ((String.fromCharCode(65536).charCodeAt(0) === 65536) || !(String.fromCharCode(256).charCodeAt(0) === 256)) {
|
|
50
49
|
throw new Error('Supported UTF-16 only!')
|
|
51
50
|
}
|
|
@@ -135,6 +134,9 @@ SOFTWARE.
|
|
|
135
134
|
'JA': 9,
|
|
136
135
|
'Telu': 10,
|
|
137
136
|
'MR': 11,
|
|
137
|
+
'B': 12,
|
|
138
|
+
'E': 13,
|
|
139
|
+
'AR': 14
|
|
138
140
|
};
|
|
139
141
|
_JSSC.BASE = function() { /* Base */
|
|
140
142
|
const chrsBase = charsBase();
|
|
@@ -397,6 +399,29 @@ SOFTWARE.
|
|
|
397
399
|
}
|
|
398
400
|
return chrsBase;
|
|
399
401
|
};
|
|
402
|
+
_JSSC.B = function() { /* Baltic */
|
|
403
|
+
const chrsBase = charsLatin();
|
|
404
|
+
for (let i = 0x100; i < 0x17F; i++) {
|
|
405
|
+
chrsBase[i - 128] = _JSSC._char(i);
|
|
406
|
+
}
|
|
407
|
+
chrsBase[255] = _JSSC._char(0x17F); /* U+017F */
|
|
408
|
+
return chrsBase;
|
|
409
|
+
};
|
|
410
|
+
_JSSC.E = function() { /* European */
|
|
411
|
+
const chrsBase = charsLatin();
|
|
412
|
+
for (let i = 0x80; i < 0xFF; i++) {
|
|
413
|
+
chrsBase[i] = _JSSC._char(i);
|
|
414
|
+
}
|
|
415
|
+
chrsBase[255] = _JSSC._char(0x17F); /* U+017F */
|
|
416
|
+
return chrsBase;
|
|
417
|
+
};
|
|
418
|
+
_JSSC.AR = function() { /* Arabic */
|
|
419
|
+
const chrsBase = {};
|
|
420
|
+
for (let i = 0x600; i < 0x6FF; i++) {
|
|
421
|
+
chrsBase[i - 1536] = _JSSC._char(i);
|
|
422
|
+
}
|
|
423
|
+
return chrsBase;
|
|
424
|
+
}
|
|
400
425
|
_JSSC.use = class {
|
|
401
426
|
constructor() {
|
|
402
427
|
let output = {};
|
|
@@ -417,7 +442,8 @@ SOFTWARE.
|
|
|
417
442
|
function cryptCharCode(
|
|
418
443
|
code, get = false,
|
|
419
444
|
repeatBefore = false, repeatAfter = false,
|
|
420
|
-
beginId = -1, code2 = 0, sequences = false
|
|
445
|
+
beginId = -1, code2 = 0, sequences = false,
|
|
446
|
+
code3 = -1
|
|
421
447
|
) {
|
|
422
448
|
if (get) {
|
|
423
449
|
const codeBin = decToBin(code, 16);
|
|
@@ -431,23 +457,38 @@ SOFTWARE.
|
|
|
431
457
|
beginId: codeSet[2] === '1' ? begid : -1,
|
|
432
458
|
code2: binToDec(codeBin.slice(0,4)),
|
|
433
459
|
sequences: codeBin.slice(4,5) === '1',
|
|
434
|
-
code3: codeSet[2] === '0' ? begid : -1,
|
|
460
|
+
code3: codeSet[2] === '0' ? begid : -1,
|
|
435
461
|
bin: codeBin,
|
|
436
462
|
}
|
|
437
463
|
} else {
|
|
438
|
-
const sixteenBits =
|
|
439
|
-
|
|
440
|
-
decToBin(code2, 4) +
|
|
441
|
-
(sequences ? '1' : '0') +
|
|
442
|
-
(beginId >= 0 ?
|
|
443
|
-
(repeatBefore ? '1' : '0') +
|
|
444
|
-
(repeatAfter ? '1' : '0') +
|
|
445
|
-
(beginId >= 0 ? '1' : '0') +
|
|
446
|
-
decToBin(code, 5);
|
|
464
|
+
const sixteenBits = /* 16-bit Data/Header character */
|
|
465
|
+
|
|
466
|
+
decToBin(code2, 4) + /* Bits 0-3 : code2 */
|
|
467
|
+
(sequences ? '1' : '0') + /* Bit 4 : sequences?|odd? */
|
|
468
|
+
decToBin(beginId >= 0 ? beginId : code3 < 0 ? 0 : code3, 3) + /* Bits 5-7 : beginID | code3 */
|
|
469
|
+
(repeatBefore ? '1' : '0') + /* Bit 8 : inp RLE? | num? */
|
|
470
|
+
(repeatAfter ? '1' : '0') + /* Bit 9 : output RLE? */
|
|
471
|
+
(beginId >= 0 ? '1' : '0') + /* Bit 10 : beginID? */
|
|
472
|
+
decToBin(code, 5); /* Bits 11-15 : code1 */
|
|
447
473
|
|
|
448
474
|
return binToDec(sixteenBits);
|
|
449
475
|
}
|
|
450
476
|
}
|
|
477
|
+
/* Code 1 usage table */
|
|
478
|
+
/* ------------------ */
|
|
479
|
+
/* 00: No compression */
|
|
480
|
+
/* 01: CharCode.len=2 */
|
|
481
|
+
/* 02: ASCII in UTF16 */ /* Yeah ik thats not actually matches ASCII */
|
|
482
|
+
/* 03: Integers (Any) */
|
|
483
|
+
/* 04: Build Alphabet */
|
|
484
|
+
/* 05: Char Encodings */
|
|
485
|
+
/* 06: Integers (>15) */
|
|
486
|
+
/* 07: Frequency Map */
|
|
487
|
+
/* 08: URL to binary */
|
|
488
|
+
/* 09: Segmt compress */
|
|
489
|
+
/* 10: Repeating strs */
|
|
490
|
+
/* 11 - 30: Reserved */
|
|
491
|
+
/* 31: Rcrsv compress */
|
|
451
492
|
|
|
452
493
|
const SEQUENCE_MARKER = '\uDBFF'; /* Private Use Area */
|
|
453
494
|
|
|
@@ -598,95 +639,395 @@ SOFTWARE.
|
|
|
598
639
|
return result;
|
|
599
640
|
}
|
|
600
641
|
|
|
642
|
+
const freqMap = {
|
|
643
|
+
ESCAPE_BYTE: 0xFF,
|
|
644
|
+
TOP_COUNT: 254,
|
|
645
|
+
SPLITTER: " \u200B",
|
|
646
|
+
|
|
647
|
+
compress(text, splitter = this.SPLITTER) {
|
|
648
|
+
const freq = {};
|
|
649
|
+
for (let char of text) {
|
|
650
|
+
freq[char] = (freq[char] || 0) + 1;
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
const topChars = Object.entries(freq)
|
|
654
|
+
.sort((a, b) => b[1] - a[1])
|
|
655
|
+
.slice(0, this.TOP_COUNT)
|
|
656
|
+
.map(entry => entry[0]);
|
|
657
|
+
|
|
658
|
+
const charToIndex = new Map(topChars.map((char, i) => [char, i]));
|
|
659
|
+
|
|
660
|
+
let header = String.fromCharCode(topChars.length) + topChars.join('');
|
|
661
|
+
|
|
662
|
+
let bytes = [];
|
|
663
|
+
for (let char of text) {
|
|
664
|
+
if (charToIndex.has(char)) {
|
|
665
|
+
/* frequent */
|
|
666
|
+
bytes.push(charToIndex.get(char));
|
|
667
|
+
} else {
|
|
668
|
+
/* rare */
|
|
669
|
+
bytes.push(this.ESCAPE_BYTE);
|
|
670
|
+
const code = char.charCodeAt(0);
|
|
671
|
+
bytes.push((code >> 8) & 0xFF);
|
|
672
|
+
bytes.push(code & 0xFF);
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
|
|
676
|
+
/* to UTF16 */
|
|
677
|
+
let compressedBody = "";
|
|
678
|
+
for (let i = 0; i < bytes.length; i += 2) {
|
|
679
|
+
const b1 = bytes[i];
|
|
680
|
+
const b2 = (i + 1 < bytes.length) ? bytes[i + 1] : 0x00;
|
|
681
|
+
compressedBody += String.fromCharCode((b1 << 8) | b2);
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
return header + splitter + compressedBody;
|
|
685
|
+
},
|
|
686
|
+
|
|
687
|
+
decompress(compressedText, splitter = this.SPLITTER) {
|
|
688
|
+
const parts = compressedText.split(splitter);
|
|
689
|
+
|
|
690
|
+
if (parts.length < 2) {
|
|
691
|
+
throw new Error('Invalid freqMap data: splitter not found');
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
const headerPart = parts[0];
|
|
695
|
+
const bodyPart = parts.slice(1).join(splitter);
|
|
696
|
+
|
|
697
|
+
const topCount = headerPart.charCodeAt(0);
|
|
698
|
+
const topChars = headerPart.substring(1, topCount + 1);
|
|
699
|
+
|
|
700
|
+
let bytes = [];
|
|
701
|
+
for (let i = 0; i < bodyPart.length; i++) {
|
|
702
|
+
const code = bodyPart.charCodeAt(i);
|
|
703
|
+
bytes.push((code >> 8) & 0xFF);
|
|
704
|
+
bytes.push(code & 0xFF);
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
let result = "";
|
|
708
|
+
for (let i = 0; i < bytes.length; i++) {
|
|
709
|
+
const b = bytes[i];
|
|
710
|
+
if (b === this.ESCAPE_BYTE) {
|
|
711
|
+
const charCode = (bytes[i + 1] << 8) | bytes[i + 2];
|
|
712
|
+
result += String.fromCharCode(charCode);
|
|
713
|
+
i += 2;
|
|
714
|
+
} else if (b < topCount) {
|
|
715
|
+
result += topChars[b];
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
return result;
|
|
719
|
+
},
|
|
720
|
+
|
|
721
|
+
/**
|
|
722
|
+
* 0 = Fail
|
|
723
|
+
* 1 = Success
|
|
724
|
+
* 2 = Remove last character (Success)
|
|
725
|
+
* @param {string} text
|
|
726
|
+
* @param {string?} splitter
|
|
727
|
+
* @returns {number|[number, number, string, string]}
|
|
728
|
+
*/
|
|
729
|
+
test(text, splitter = this.SPLITTER) {
|
|
730
|
+
try {
|
|
731
|
+
if (text.includes(splitter)) return 0;
|
|
732
|
+
const packed = this.compress(text, splitter);
|
|
733
|
+
const unpacked = this.decompress(packed, splitter);
|
|
734
|
+
if (packed.length < text.length) {
|
|
735
|
+
if (unpacked == text) return [1, packed.length, splitter, packed];
|
|
736
|
+
else if (unpacked.slice(0,-1) == text) return [2, packed.length, splitter, packed];
|
|
737
|
+
else return 0;
|
|
738
|
+
}
|
|
739
|
+
return 0;
|
|
740
|
+
} catch (_) {
|
|
741
|
+
return 0;
|
|
742
|
+
}
|
|
743
|
+
}
|
|
744
|
+
};
|
|
745
|
+
|
|
746
|
+
const freqMapSplitters = [
|
|
747
|
+
" \u200B","\u0000",
|
|
748
|
+
"\u001F", "\u0001",
|
|
749
|
+
"\uFFFD", "\u2022",
|
|
750
|
+
"|§|", "\uFEFF"
|
|
751
|
+
];
|
|
752
|
+
|
|
753
|
+
function segments(str) {
|
|
754
|
+
if (typeof str !== 'string' || str.length === 0) return [];
|
|
755
|
+
|
|
756
|
+
const THRESHOLD = 128;
|
|
757
|
+
const segs = [];
|
|
758
|
+
let currentSeg = str[0];
|
|
759
|
+
|
|
760
|
+
for (let i = 1; i < str.length; i++) {
|
|
761
|
+
const prevCode = str.charCodeAt(i - 1);
|
|
762
|
+
const currCode = str.charCodeAt(i);
|
|
763
|
+
|
|
764
|
+
if (Math.abs(currCode - prevCode) > THRESHOLD) {
|
|
765
|
+
segs.push(currentSeg);
|
|
766
|
+
currentSeg = str[i];
|
|
767
|
+
} else {
|
|
768
|
+
currentSeg += str[i];
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
|
|
772
|
+
if (currentSeg) segs.push(currentSeg);
|
|
773
|
+
|
|
774
|
+
return segs;
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
async function tryRecursive(base, opts) {
|
|
778
|
+
if (!opts.recursivecompression) return base;
|
|
779
|
+
|
|
780
|
+
let cur = base;
|
|
781
|
+
let depth = 0;
|
|
782
|
+
|
|
783
|
+
while (depth < 15) {
|
|
784
|
+
depth++;
|
|
785
|
+
const next = await compress(cur, {
|
|
786
|
+
...opts,
|
|
787
|
+
recursivecompression: false
|
|
788
|
+
});
|
|
789
|
+
|
|
790
|
+
if (next.length >= cur.length) break;
|
|
791
|
+
|
|
792
|
+
const dec = await decompress(next, true);
|
|
793
|
+
if (dec !== cur) break;
|
|
794
|
+
|
|
795
|
+
cur = next;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
if (depth === 0) return null;
|
|
799
|
+
|
|
800
|
+
return (
|
|
801
|
+
charCode(
|
|
802
|
+
cryptCharCode(
|
|
803
|
+
31,
|
|
804
|
+
false,
|
|
805
|
+
false,
|
|
806
|
+
false,
|
|
807
|
+
-1,
|
|
808
|
+
depth,
|
|
809
|
+
false,
|
|
810
|
+
-1
|
|
811
|
+
)
|
|
812
|
+
) + cur
|
|
813
|
+
);
|
|
814
|
+
}
|
|
815
|
+
|
|
601
816
|
/**
|
|
602
817
|
* **JavaScript String Compressor - compress function.**
|
|
603
|
-
* @param {string}
|
|
604
|
-
* @
|
|
605
|
-
* @
|
|
818
|
+
* @param {string|object|number} input string
|
|
819
|
+
* @param {{segmentation?: boolean, recursiveCompression?: boolean, JUSTC?: boolean}} [options]
|
|
820
|
+
* @returns {Promise<string>} Compressed string
|
|
821
|
+
* @example await compress('Hello, World!');
|
|
606
822
|
* @since 1.0.0
|
|
607
823
|
*/
|
|
608
|
-
function compress(
|
|
609
|
-
if (typeof
|
|
824
|
+
async function compress(input, options) {
|
|
825
|
+
if (typeof input != 'string' && typeof input != 'object' && typeof input != 'number') throw new Error('Invalid input.');
|
|
826
|
+
const opts = {
|
|
827
|
+
segmentation: true,
|
|
828
|
+
recursivecompression: true,
|
|
829
|
+
justc: JUSTC ? true : false,
|
|
830
|
+
};
|
|
831
|
+
|
|
832
|
+
/* Read options */
|
|
833
|
+
if (options) {
|
|
834
|
+
if (typeof options != 'object') throw new Error('Invalid options input.');
|
|
835
|
+
for (const [key, value] of Object.entries(options)) {
|
|
836
|
+
if (typeof value != 'boolean') throw new Error('Invalid options input.');
|
|
837
|
+
if (key.toLowerCase() in opts) {
|
|
838
|
+
opts[key.toLowerCase()] = value;
|
|
839
|
+
continue;
|
|
840
|
+
}
|
|
841
|
+
console.warn(`Unknown option: "${key}".`);
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
const originalInput = input;
|
|
846
|
+
let str = input;
|
|
847
|
+
let isNum = false;
|
|
848
|
+
|
|
849
|
+
if (typeof str === 'number') {
|
|
850
|
+
isNum = true;
|
|
851
|
+
str = str.toString();
|
|
852
|
+
if (str.includes('.')) throw new Error('Invalid input.');
|
|
853
|
+
}
|
|
854
|
+
|
|
610
855
|
let repeatBefore = false;
|
|
611
856
|
function repeatChars(txt) {
|
|
612
857
|
return txt.replace(/(.)\1+/g, ( a , b ) => b + a.length);
|
|
613
858
|
}
|
|
859
|
+
|
|
614
860
|
let beginId = -1;
|
|
615
|
-
for (const begin of _JSSC._begin) {
|
|
861
|
+
if (typeof str == 'string') for (const begin of _JSSC._begin) {
|
|
616
862
|
if (str.startsWith(begin)) {
|
|
617
863
|
beginId = _JSSC._begin.indexOf(begin);
|
|
618
864
|
str = str.slice(begin.length);
|
|
619
865
|
break;
|
|
620
866
|
}
|
|
867
|
+
};
|
|
868
|
+
|
|
869
|
+
let code3 = -1;
|
|
870
|
+
async function toJUSTC(obj) {
|
|
871
|
+
try {
|
|
872
|
+
const result = await JUSTC.stringify(obj);
|
|
873
|
+
if (result && typeof result.then === 'function') {
|
|
874
|
+
return await result;
|
|
875
|
+
}
|
|
876
|
+
return result;
|
|
877
|
+
} catch (_) {
|
|
878
|
+
/* Browsers */
|
|
879
|
+
await JUSTC.initialize();
|
|
880
|
+
return JUSTC.stringify(obj);
|
|
881
|
+
}
|
|
621
882
|
}
|
|
622
|
-
|
|
623
|
-
|
|
883
|
+
if (beginId == -1) {
|
|
884
|
+
/* JSON Array (as object) */
|
|
885
|
+
if (typeof str == 'object' && Array.isArray(str)) {
|
|
886
|
+
str = JSON.stringify(str).slice(1,-1);
|
|
887
|
+
code3 = 4;
|
|
888
|
+
} else
|
|
889
|
+
/* JSON Object (as object) */
|
|
890
|
+
if (typeof str == 'object') try {
|
|
891
|
+
if (opts.justc) {
|
|
892
|
+
const JUSTCobj = await toJUSTC(str);
|
|
893
|
+
str = JUSTCobj;
|
|
894
|
+
code3 = 2;
|
|
895
|
+
} else {
|
|
896
|
+
str = JSON.stringify(str);
|
|
897
|
+
code3 = 6;
|
|
898
|
+
}
|
|
899
|
+
} catch (error) {
|
|
900
|
+
const msg = new Error('Invalid input.');
|
|
901
|
+
throw new AggregateError([msg, error], msg.message);
|
|
902
|
+
} else
|
|
903
|
+
/* JSON Object (as string) */
|
|
904
|
+
try {
|
|
905
|
+
const obj = JSON.parse(str);
|
|
906
|
+
if (!Array.isArray(obj)) {
|
|
907
|
+
|
|
908
|
+
const JUSTCobj = opts.justc ? await toJUSTC(obj) : false;
|
|
909
|
+
|
|
910
|
+
if (JUSTCobj && JUSTCobj.length < str.length && str == JSON.stringify(obj)) {
|
|
911
|
+
str = JUSTCobj;
|
|
912
|
+
code3 = 1;
|
|
913
|
+
} else {
|
|
914
|
+
str = str.slice(1,-1);
|
|
915
|
+
code3 = 5;
|
|
916
|
+
}
|
|
917
|
+
} else {
|
|
918
|
+
/* JSON Array (as string) */
|
|
919
|
+
str = str.slice(1,-1);
|
|
920
|
+
code3 = 3;
|
|
921
|
+
}} catch (_) {
|
|
922
|
+
}}
|
|
923
|
+
|
|
924
|
+
if (!/\d/.test(str)) {
|
|
624
925
|
str = repeatChars(str);
|
|
625
926
|
repeatBefore = true;
|
|
626
927
|
}
|
|
627
|
-
const strdata = stringCodes(str);
|
|
628
|
-
const ascii = strdata.maxCharCode < 256;
|
|
629
|
-
let repeatAfter = false;
|
|
630
|
-
let sequences = false;
|
|
631
928
|
|
|
632
|
-
function
|
|
929
|
+
function processOutput(output, disableSeq = false) {
|
|
930
|
+
let repeatAfter = false;
|
|
931
|
+
let sequences = false;
|
|
932
|
+
|
|
633
933
|
const hasDigits = /\d/.test(output);
|
|
634
934
|
if (!hasDigits) {
|
|
635
935
|
repeatAfter = true;
|
|
636
936
|
output = repeatChars(output);
|
|
637
937
|
}
|
|
638
938
|
|
|
639
|
-
if (
|
|
939
|
+
if (!disableSeq) {
|
|
640
940
|
const compressed = compressSequences(output);
|
|
641
941
|
if (compressed.sequences) {
|
|
642
942
|
sequences = true;
|
|
643
|
-
return compressed.compressed;
|
|
943
|
+
return [compressed.compressed, repeatAfter, sequences];
|
|
644
944
|
}
|
|
645
945
|
}
|
|
646
946
|
|
|
647
|
-
return output;
|
|
947
|
+
return [output, repeatAfter, sequences];
|
|
648
948
|
}
|
|
649
949
|
|
|
650
|
-
const
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
'B': 11,
|
|
656
|
-
'C': 12,
|
|
657
|
-
'D': 13,
|
|
658
|
-
'E': 14
|
|
659
|
-
};
|
|
660
|
-
const inputt = str
|
|
661
|
-
.replaceAll('10', 'A')
|
|
662
|
-
.replaceAll('11', 'B')
|
|
663
|
-
.replaceAll('12', 'C')
|
|
664
|
-
.replaceAll('13', 'D')
|
|
665
|
-
.replaceAll('14', 'E');
|
|
666
|
-
const binOut = [];
|
|
667
|
-
for (const character of inputt.split('')) {
|
|
668
|
-
if (/\d/.test(character)) {
|
|
669
|
-
binOut.push(decToBin(parseInt(character), 4));
|
|
670
|
-
} else {
|
|
671
|
-
binOut.push(decToBin(convertNums[character], 4));
|
|
672
|
-
}
|
|
673
|
-
};
|
|
674
|
-
let output = '';
|
|
675
|
-
function binPadStart(bin) {
|
|
676
|
-
if (bin.length < 16) {
|
|
677
|
-
const numm = 4 - stringChunks(bin, 4).length;
|
|
678
|
-
return decToBin(15, 4).repeat(numm)+bin;
|
|
679
|
-
} else return bin;
|
|
950
|
+
const safeTry = async (fn) => {
|
|
951
|
+
try {
|
|
952
|
+
return await fn();
|
|
953
|
+
} catch {
|
|
954
|
+
return null;
|
|
680
955
|
}
|
|
681
|
-
|
|
682
|
-
|
|
956
|
+
};
|
|
957
|
+
|
|
958
|
+
const validate = async (compressed) => {
|
|
959
|
+
try {
|
|
960
|
+
const dec = await decompress(compressed, true);
|
|
961
|
+
return dec === String(originalInput);
|
|
962
|
+
} catch {
|
|
963
|
+
return false;
|
|
683
964
|
}
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
965
|
+
};
|
|
966
|
+
|
|
967
|
+
const candidates = [];
|
|
968
|
+
|
|
969
|
+
if (/^\d+$/.test(str)) {
|
|
970
|
+
/* Integers ( < 15 ) */
|
|
971
|
+
candidates.push(async () => {
|
|
972
|
+
const out = await (async () => {
|
|
973
|
+
const num = parseInt(str);
|
|
974
|
+
if (num < 15) {
|
|
975
|
+
return charCode(
|
|
976
|
+
cryptCharCode(isNum ? 6 : 0, false, false, false, -1, num + 1, false, code3)
|
|
977
|
+
);
|
|
978
|
+
}
|
|
979
|
+
return null;
|
|
980
|
+
})();
|
|
981
|
+
if (!out) return null;
|
|
982
|
+
if (!(await validate(out))) return null;
|
|
983
|
+
return out;
|
|
984
|
+
});
|
|
985
|
+
/* Integers (any) */
|
|
986
|
+
candidates.push(async () => {
|
|
987
|
+
const convertNums = {
|
|
988
|
+
'A': 10,
|
|
989
|
+
'B': 11,
|
|
990
|
+
'C': 12,
|
|
991
|
+
'D': 13,
|
|
992
|
+
'E': 14
|
|
993
|
+
};
|
|
994
|
+
const inputt = str
|
|
995
|
+
.replaceAll('10', 'A')
|
|
996
|
+
.replaceAll('11', 'B')
|
|
997
|
+
.replaceAll('12', 'C')
|
|
998
|
+
.replaceAll('13', 'D')
|
|
999
|
+
.replaceAll('14', 'E');
|
|
1000
|
+
const binOut = [];
|
|
1001
|
+
for (const character of inputt.split('')) {
|
|
1002
|
+
if (/\d/.test(character)) {
|
|
1003
|
+
binOut.push(decToBin(parseInt(character), 4));
|
|
1004
|
+
} else {
|
|
1005
|
+
binOut.push(decToBin(convertNums[character], 4));
|
|
1006
|
+
}
|
|
1007
|
+
};
|
|
1008
|
+
let [output, RLE, sequences] = ['', false, false];
|
|
1009
|
+
function binPadStart(bin) {
|
|
1010
|
+
if (bin.length < 16) {
|
|
1011
|
+
const numm = 4 - stringChunks(bin, 4).length;
|
|
1012
|
+
return decToBin(15, 4).repeat(numm)+bin;
|
|
1013
|
+
} else return bin;
|
|
1014
|
+
}
|
|
1015
|
+
for (const character of chunkArray(binOut, 4)) {
|
|
1016
|
+
output += String.fromCharCode(binToDec(binPadStart(character.join(''))));
|
|
1017
|
+
}
|
|
1018
|
+
[output, RLE, sequences] = processOutput(output);
|
|
1019
|
+
output = charCode(cryptCharCode(3, false, isNum, RLE, -1, 0, sequences, code3)) + output;
|
|
1020
|
+
if (!(await validate(output))) return null;
|
|
1021
|
+
return output;
|
|
1022
|
+
});
|
|
1023
|
+
}
|
|
1024
|
+
|
|
1025
|
+
candidates.push(async () => {
|
|
1026
|
+
const strdata = stringCodes(str);
|
|
1027
|
+
if (!(strdata.max === 2 && strdata.min === 2)) return null;
|
|
1028
|
+
|
|
688
1029
|
let chars = strdata.output;
|
|
689
|
-
let output = '';
|
|
1030
|
+
let [output, repeatAfter, seq] = ['', false, false];
|
|
690
1031
|
function addChar(codee) {
|
|
691
1032
|
output += String.fromCharCode(codee);
|
|
692
1033
|
}
|
|
@@ -717,42 +1058,32 @@ SOFTWARE.
|
|
|
717
1058
|
}
|
|
718
1059
|
}
|
|
719
1060
|
}
|
|
720
|
-
output =
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
/*
|
|
726
|
-
Bytes
|
|
727
|
-
|
|
728
|
-
---
|
|
1061
|
+
[output, repeatAfter, seq] = processOutput(out);
|
|
1062
|
+
const res = charCode(cryptCharCode(1, false, repeatBefore, repeatAfter, beginId, 0, seq, code3)) + output;
|
|
1063
|
+
if (!(await validate(res))) return null;
|
|
1064
|
+
return res;
|
|
1065
|
+
});
|
|
729
1066
|
|
|
730
|
-
|
|
731
|
-
|
|
1067
|
+
/* ASCII in UTF-16 */
|
|
1068
|
+
candidates.push(async () => {
|
|
1069
|
+
const strdata = stringCodes(str);
|
|
1070
|
+
if (strdata.maxCharCode >= 256) return null;
|
|
732
1071
|
|
|
733
|
-
|
|
1072
|
+
let [out, repeatAfter, seq] = ['', false, false];
|
|
1073
|
+
for (const pair of stringChunks(str, 2)) {
|
|
1074
|
+
let bin = '';
|
|
1075
|
+
for (const c of pair) bin += decToBin(c.charCodeAt(0), 8);
|
|
1076
|
+
out += String.fromCharCode(binToDec(bin));
|
|
1077
|
+
}
|
|
734
1078
|
|
|
735
|
-
|
|
1079
|
+
[out, repeatAfter, seq] = processOutput(out);
|
|
1080
|
+
const res = charCode(cryptCharCode(2, false, repeatBefore, repeatAfter, beginId, 0, seq, code3)) + out;
|
|
1081
|
+
if (!(await validate(res))) return null;
|
|
1082
|
+
return res;
|
|
1083
|
+
});
|
|
736
1084
|
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
*/
|
|
740
|
-
const twoChars = [];
|
|
741
|
-
let output = '';
|
|
742
|
-
for (const chars_ of stringChunks(str, 2)) {
|
|
743
|
-
let charsCode = '';
|
|
744
|
-
for (const char of chars_.split('')) {
|
|
745
|
-
charsCode += decToBin(char.charCodeAt(0), 8);
|
|
746
|
-
}
|
|
747
|
-
twoChars.push(charsCode);
|
|
748
|
-
}
|
|
749
|
-
for (const char of twoChars) {
|
|
750
|
-
output += String.fromCharCode(binToDec(char));
|
|
751
|
-
}
|
|
752
|
-
output = processCompression(output);
|
|
753
|
-
return charCode(cryptCharCode(2, false, repeatBefore, repeatAfter, beginId, 0, sequences)) + output;
|
|
754
|
-
|
|
755
|
-
} else {
|
|
1085
|
+
/* Character encodings */
|
|
1086
|
+
candidates.push(async () => {
|
|
756
1087
|
const characterEncodings = new _JSSC.use();
|
|
757
1088
|
const stringArray = str.split('');
|
|
758
1089
|
let useCharacterEncoding;
|
|
@@ -789,61 +1120,239 @@ SOFTWARE.
|
|
|
789
1120
|
for (const binCharCodes of chunkArray(binaryCharCodes, 2)) {
|
|
790
1121
|
convertCharCodes.push(binCharCodes.join('').padStart(16, '0'));
|
|
791
1122
|
}
|
|
792
|
-
let outputStr = '';
|
|
1123
|
+
let [outputStr, repeatAfter, seq] = ['', false, false];
|
|
793
1124
|
for (const characterCode of convertCharCodes) {
|
|
794
1125
|
outputStr += String.fromCharCode(binToDec(characterCode))
|
|
795
1126
|
}
|
|
796
|
-
|
|
797
|
-
outputStr =
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
}
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
1127
|
+
|
|
1128
|
+
[outputStr, repeatAfter, seq] = processOutput(outputStr);
|
|
1129
|
+
outputStr = charCode(cryptCharCode(5, false, repeatBefore, repeatAfter, beginId, charEncodingID, seq, code3)) + outputStr;
|
|
1130
|
+
if (await validate(outputStr)) return outputStr;
|
|
1131
|
+
}
|
|
1132
|
+
return null;
|
|
1133
|
+
});
|
|
1134
|
+
|
|
1135
|
+
/* Build alphabet */
|
|
1136
|
+
candidates.push(async () => {
|
|
1137
|
+
const uniq = [...new Set(str.split('').map(c => c.charCodeAt(0)))];
|
|
1138
|
+
if (uniq.length >= 16) return null;
|
|
1139
|
+
|
|
1140
|
+
let out = uniq.map(c => String.fromCharCode(c)).join('');
|
|
1141
|
+
let buf = [];
|
|
1142
|
+
let [repeatAfter, seq] = [false, false];
|
|
1143
|
+
|
|
1144
|
+
for (const c of str) {
|
|
1145
|
+
buf.push(uniq.indexOf(c.charCodeAt(0)));
|
|
1146
|
+
if (buf.length === 4) {
|
|
1147
|
+
out += String.fromCharCode(binToDec(buf.map(n => decToBin(n, 4)).join('')));
|
|
1148
|
+
buf = [];
|
|
806
1149
|
}
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
if (buf.length) {
|
|
1153
|
+
out += String.fromCharCode(
|
|
1154
|
+
binToDec(buf.map(n => decToBin(n, 4)).join('').padStart(16, '1'))
|
|
1155
|
+
);
|
|
1156
|
+
}
|
|
1157
|
+
|
|
1158
|
+
[out, repeatAfter, seq] = processOutput(out);
|
|
1159
|
+
const res = charCode(cryptCharCode(4, false, repeatBefore, repeatAfter, beginId, uniq.length, seq, code3)) + out;
|
|
1160
|
+
if (!(await validate(res))) return null;
|
|
1161
|
+
return res;
|
|
1162
|
+
});
|
|
1163
|
+
|
|
1164
|
+
/* Frequency map */
|
|
1165
|
+
candidates.push(async () => {
|
|
1166
|
+
for (const splitter of freqMapSplitters) {
|
|
1167
|
+
const test = freqMap.test(str, splitter);
|
|
1168
|
+
if (!Array.isArray(test)) continue;
|
|
1169
|
+
|
|
1170
|
+
const [, , sp, packed] = test;
|
|
1171
|
+
const code2 = binToDec((test[0] - 1).toString() + decToBin(freqMapSplitters.indexOf(sp), 3));
|
|
1172
|
+
const res = charCode(cryptCharCode(7, false, false, false, -1, code2)) + packed;
|
|
1173
|
+
|
|
1174
|
+
if (await validate(res)) return res;
|
|
1175
|
+
}
|
|
1176
|
+
return null;
|
|
1177
|
+
});
|
|
1178
|
+
|
|
1179
|
+
/* URL */
|
|
1180
|
+
candidates.push(async () => {
|
|
1181
|
+
if (typeof str !== 'string') return null;
|
|
1182
|
+
|
|
1183
|
+
let url;
|
|
1184
|
+
try {
|
|
1185
|
+
url = new URL(_JSSC._begin[beginId] + str);
|
|
1186
|
+
} catch {
|
|
1187
|
+
return null;
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
const originalHref = url.href;
|
|
1191
|
+
|
|
1192
|
+
let hasPercent = /%[0-9A-Fa-f]{2}/.test(originalHref);
|
|
1193
|
+
let hasPunycode = url.hostname.includes('xn--');
|
|
1194
|
+
let hasQuery = !!url.search;
|
|
1195
|
+
let hasFragment = !!url.hash;
|
|
1196
|
+
|
|
1197
|
+
/* normalize */
|
|
1198
|
+
let normalized = originalHref.slice(_JSSC._begin[beginId].length);
|
|
1199
|
+
|
|
1200
|
+
/* punycode to unicode */
|
|
1201
|
+
if (hasPunycode && typeof punycode !== 'undefined') {
|
|
1202
|
+
url.hostname = punycode.toUnicode(url.hostname);
|
|
1203
|
+
normalized = url.href.slice(_JSSC._begin[beginId].length);
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
/* percent to bytes */
|
|
1207
|
+
let bytes = [];
|
|
1208
|
+
for (let i = 0; i < normalized.length; i++) {
|
|
1209
|
+
const ch = normalized[i];
|
|
1210
|
+
if (ch === '%' && i + 2 < normalized.length) {
|
|
1211
|
+
const hex = normalized.slice(i + 1, i + 3);
|
|
1212
|
+
if (/^[0-9A-Fa-f]{2}$/.test(hex)) {
|
|
1213
|
+
bytes.push(parseInt(hex, 16));
|
|
1214
|
+
i += 2;
|
|
1215
|
+
continue;
|
|
819
1216
|
}
|
|
820
|
-
char_.push(index);
|
|
821
1217
|
}
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
1218
|
+
bytes.push(normalized.charCodeAt(i));
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1221
|
+
let odd = bytes.length & 1;
|
|
1222
|
+
if (odd) bytes.push(0);
|
|
1223
|
+
|
|
1224
|
+
/* bytes to UTF16 */
|
|
1225
|
+
let out = '';
|
|
1226
|
+
for (let i = 0; i < bytes.length; i += 2) {
|
|
1227
|
+
out += String.fromCharCode(
|
|
1228
|
+
(bytes[i] << 8) | (bytes[i + 1] ?? 0)
|
|
1229
|
+
);
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
let code2 =
|
|
1233
|
+
(hasPercent ? 1 : 0) |
|
|
1234
|
+
(hasPunycode ? 2 : 0) |
|
|
1235
|
+
(hasQuery ? 4 : 0) |
|
|
1236
|
+
(hasFragment ? 8 : 0);
|
|
1237
|
+
|
|
1238
|
+
let repeatAfter = false;
|
|
1239
|
+
[out, repeatAfter,] = processOutput(out, true);
|
|
1240
|
+
|
|
1241
|
+
const res =
|
|
1242
|
+
charCode(
|
|
1243
|
+
cryptCharCode(
|
|
1244
|
+
8,
|
|
1245
|
+
false,
|
|
1246
|
+
repeatBefore,
|
|
1247
|
+
repeatAfter,
|
|
1248
|
+
beginId,
|
|
1249
|
+
code2,
|
|
1250
|
+
odd,
|
|
1251
|
+
code3
|
|
1252
|
+
)
|
|
1253
|
+
) + out;
|
|
1254
|
+
|
|
1255
|
+
if (!(await validate(res))) return null;
|
|
1256
|
+
return res;
|
|
1257
|
+
});
|
|
1258
|
+
|
|
1259
|
+
/* Segmentation */
|
|
1260
|
+
if (opts.segmentation) candidates.push(async () => {
|
|
1261
|
+
const segs = segments(str);
|
|
1262
|
+
|
|
1263
|
+
if (segs.length < 2) return null;
|
|
1264
|
+
|
|
1265
|
+
let out = segs.length - 2 < 15 ? '' : String.fromCharCode(segs.length - 2);
|
|
1266
|
+
|
|
1267
|
+
for (const seg of segs) {
|
|
1268
|
+
const segOpts = {
|
|
1269
|
+
...opts,
|
|
1270
|
+
segmentation: false
|
|
829
1271
|
}
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
1272
|
+
const compressed = await compress(seg, segOpts);
|
|
1273
|
+
|
|
1274
|
+
out += String.fromCharCode(seg.length);
|
|
1275
|
+
out += compressed;
|
|
833
1276
|
}
|
|
834
1277
|
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
1278
|
+
const res =
|
|
1279
|
+
charCode(
|
|
1280
|
+
cryptCharCode(
|
|
1281
|
+
9,
|
|
1282
|
+
false,
|
|
1283
|
+
repeatBefore,
|
|
1284
|
+
opts.justc,
|
|
1285
|
+
beginId,
|
|
1286
|
+
Math.min(segs.length - 2, 15),
|
|
1287
|
+
opts.recursivecompression,
|
|
1288
|
+
code3
|
|
1289
|
+
)
|
|
1290
|
+
) + out;
|
|
1291
|
+
|
|
1292
|
+
if (!(await validate(res))) return null;
|
|
1293
|
+
return res;
|
|
1294
|
+
});
|
|
1295
|
+
|
|
1296
|
+
/* Repeating string */
|
|
1297
|
+
const rcheck = str.match(/^(.{1,7}?)(?:\1)+$/);
|
|
1298
|
+
if (rcheck) candidates.push(async () => {
|
|
1299
|
+
const main = rcheck[1];
|
|
1300
|
+
const count = str.length / main.length;
|
|
1301
|
+
if (Math.floor(count) != count || count < 1 || count > 65535 + 15) return null;
|
|
1302
|
+
let [out, repeatAfter, seq] = ['', false, false];
|
|
1303
|
+
[out, repeatAfter, seq] = processOutput(main);
|
|
1304
|
+
|
|
1305
|
+
const res =
|
|
1306
|
+
charCode(
|
|
1307
|
+
cryptCharCode(
|
|
1308
|
+
10,
|
|
1309
|
+
false,
|
|
1310
|
+
repeatBefore,
|
|
1311
|
+
repeatAfter,
|
|
1312
|
+
beginId,
|
|
1313
|
+
Math.min(count - 1, 15),
|
|
1314
|
+
seq,
|
|
1315
|
+
code3
|
|
1316
|
+
)
|
|
1317
|
+
) + (
|
|
1318
|
+
(count - 1) > 14 ? String.fromCharCode(count - 15) : ''
|
|
1319
|
+
) + out;
|
|
1320
|
+
|
|
1321
|
+
if (!(await validate(res))) return null;
|
|
1322
|
+
return res;
|
|
1323
|
+
});
|
|
1324
|
+
|
|
1325
|
+
/* run all */
|
|
1326
|
+
const results = (await Promise.all(candidates.map(fn => safeTry(fn))))
|
|
1327
|
+
.filter(r => typeof r === 'string' && r.length <= String(str).length);
|
|
1328
|
+
|
|
1329
|
+
let best;
|
|
1330
|
+
if (!results.length) {
|
|
1331
|
+
let [repeatAfter, sequences] = [false, false];
|
|
1332
|
+
const savedStr = str;
|
|
1333
|
+
[str, repeatAfter, sequences] = processOutput(str);
|
|
1334
|
+
if (await validate(str)) best = charCode(cryptCharCode(0, false, repeatBefore, repeatAfter, beginId, 0, sequences, code3)) + str;
|
|
1335
|
+
else best = charCode(cryptCharCode(0, false, repeatBefore, false, beginId, 0, false, code3)) + savedStr;
|
|
1336
|
+
} else best = results.reduce((a, b) => (b.length < a.length ? b : a));
|
|
1337
|
+
|
|
1338
|
+
if (opts.recursivecompression) try {
|
|
1339
|
+
for (const r of results) {
|
|
1340
|
+
const rc = await tryRecursive(r, opts);
|
|
1341
|
+
if (rc && rc.length <= best.length && await validate(rc)) {
|
|
1342
|
+
best = rc;
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
} catch (_){};
|
|
1346
|
+
|
|
1347
|
+
return best;
|
|
839
1348
|
}
|
|
840
1349
|
|
|
841
|
-
function characterEncodings(
|
|
1350
|
+
function characterEncodings(id, realstr) {
|
|
842
1351
|
const strcode2charencoding = {};
|
|
843
1352
|
for (const [name, code] of Object.entries(_JSSC._IDs)) {
|
|
844
1353
|
strcode2charencoding[code] = name
|
|
845
1354
|
}
|
|
846
|
-
const possibleCharEncoding = strcode2charencoding[
|
|
1355
|
+
const possibleCharEncoding = strcode2charencoding[id];
|
|
847
1356
|
if (possibleCharEncoding) {
|
|
848
1357
|
const characterEncodings_ = new _JSSC.use();
|
|
849
1358
|
const characterEncoding = characterEncodings_['JSSC'+possibleCharEncoding]();
|
|
@@ -866,13 +1375,43 @@ SOFTWARE.
|
|
|
866
1375
|
}
|
|
867
1376
|
}
|
|
868
1377
|
|
|
1378
|
+
async function parseJUSTC(str) {
|
|
1379
|
+
try {
|
|
1380
|
+
const result = JUSTC.parse(str);
|
|
1381
|
+
|
|
1382
|
+
if (result && typeof result.then === 'function') {
|
|
1383
|
+
return await result;
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
return result;
|
|
1387
|
+
} catch (err) {
|
|
1388
|
+
if (typeof window !== 'undefined') { /* Browsers */
|
|
1389
|
+
try {
|
|
1390
|
+
await JUSTC.initialize();
|
|
1391
|
+
|
|
1392
|
+
const retry = JUSTC.parse(str);
|
|
1393
|
+
if (retry && typeof retry.then === 'function') {
|
|
1394
|
+
return await retry;
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
return retry;
|
|
1398
|
+
} catch {
|
|
1399
|
+
return null;
|
|
1400
|
+
}
|
|
1401
|
+
}
|
|
1402
|
+
|
|
1403
|
+
return null;
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
|
|
869
1407
|
/**
|
|
870
1408
|
* **JavaScript String Compressor - decompress function.**
|
|
871
1409
|
* @param {string} str Compressed string
|
|
872
|
-
* @
|
|
1410
|
+
* @param {boolean?} [stringify] Return only string in any way
|
|
1411
|
+
* @returns {Promise<string|object|number>} Decompressed string/object/integer
|
|
873
1412
|
* @since 1.0.0
|
|
874
1413
|
*/
|
|
875
|
-
function decompress(str) {
|
|
1414
|
+
async function decompress(str, stringify = false) {
|
|
876
1415
|
if (typeof str != 'string') throw new Error('Invalid input.');
|
|
877
1416
|
const strcodes = cryptCharCode(str.charCodeAt(0) - 32, true);
|
|
878
1417
|
const strcode = strcodes.code;
|
|
@@ -883,31 +1422,51 @@ SOFTWARE.
|
|
|
883
1422
|
|
|
884
1423
|
/* sequences */
|
|
885
1424
|
let realstr = str.slice(1);
|
|
886
|
-
if (strcodes.sequences) {
|
|
1425
|
+
if (strcodes.sequences && strcode != 8 && strcode != 9) {
|
|
887
1426
|
realstr = decompressSequences(realstr);
|
|
888
1427
|
}
|
|
889
1428
|
|
|
890
1429
|
/* RLE */
|
|
891
|
-
if (strcodes.repeatAfter) {
|
|
1430
|
+
if (strcodes.repeatAfter && strcode != 9) {
|
|
892
1431
|
realstr = repeatChars(realstr);
|
|
893
1432
|
}
|
|
894
1433
|
|
|
895
|
-
function begin(out) {
|
|
1434
|
+
async function begin(out) {
|
|
896
1435
|
if (strcodes.beginId >= 0) {
|
|
897
1436
|
return _JSSC._begin[strcodes.beginId] + out;
|
|
1437
|
+
} else if (strcodes.code3 == 1 || strcodes.code3 == 2) {
|
|
1438
|
+
/* JSON Object */
|
|
1439
|
+
const result = await parseJUSTC(out);
|
|
1440
|
+
if (result && typeof result.then === 'function') {
|
|
1441
|
+
return JSON.stringify(await result);
|
|
1442
|
+
} else return JSON.stringify(result);
|
|
898
1443
|
} else return out;
|
|
899
1444
|
}
|
|
900
1445
|
|
|
901
|
-
function processOutput(out) {
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
1446
|
+
async function processOutput(out) {
|
|
1447
|
+
let output = out;
|
|
1448
|
+
if (strcodes.repeatBefore && strcode != 3) {
|
|
1449
|
+
output = repeatChars(await begin(out));
|
|
1450
|
+
} else output = await begin(out);
|
|
1451
|
+
|
|
1452
|
+
if ((strcodes.repeatBefore && strcode == 3) || strcode == 30) output = parseInt(output); else { /* Integer */
|
|
1453
|
+
if (strcodes.code3 == 3 || strcodes.code3 == 4) output = '[' + output + ']'; /* JSON Array */
|
|
1454
|
+
else if (strcodes.code3 == 5) output = '{' + output + '}'; /* JSON Object (as string) */
|
|
1455
|
+
if (strcodes.code3 == 2 || strcodes.code3 == 4 || strcodes.code3 == 6) output = JSON.parse(output);} /* JSON Object/Array (as object) */
|
|
1456
|
+
|
|
1457
|
+
if (stringify) {
|
|
1458
|
+
if (typeof output == 'object') output = JSON.stringify(output);
|
|
1459
|
+
else if (typeof output == 'number') output = output.toString();
|
|
1460
|
+
}
|
|
1461
|
+
|
|
1462
|
+
return output;
|
|
905
1463
|
}
|
|
906
1464
|
|
|
907
1465
|
let output = '';
|
|
908
1466
|
switch (strcode) {
|
|
909
|
-
case 0:
|
|
910
|
-
return processOutput(
|
|
1467
|
+
case 0: case 6:
|
|
1468
|
+
if (strcodes.code2 > 0) return await processOutput(String(strcodes.code2 - 1));
|
|
1469
|
+
return await processOutput(realstr);
|
|
911
1470
|
case 1:
|
|
912
1471
|
function addChar(cde) {
|
|
913
1472
|
output += String.fromCharCode(cde);
|
|
@@ -923,7 +1482,7 @@ SOFTWARE.
|
|
|
923
1482
|
addChar(char.charCodeAt(0));
|
|
924
1483
|
}
|
|
925
1484
|
}
|
|
926
|
-
return processOutput(output);
|
|
1485
|
+
return await processOutput(output);
|
|
927
1486
|
case 2:
|
|
928
1487
|
function toChar(binCode) {
|
|
929
1488
|
return String.fromCharCode(binToDec(binCode));
|
|
@@ -939,7 +1498,7 @@ SOFTWARE.
|
|
|
939
1498
|
output += toChar(binCode8);
|
|
940
1499
|
}
|
|
941
1500
|
}
|
|
942
|
-
return processOutput(output);
|
|
1501
|
+
return await processOutput(output);
|
|
943
1502
|
case 3:
|
|
944
1503
|
for (const char of realstr.split('')) {
|
|
945
1504
|
const binCodes = stringChunks(decToBin(char.charCodeAt(0), 16), 4);
|
|
@@ -950,7 +1509,7 @@ SOFTWARE.
|
|
|
950
1509
|
}
|
|
951
1510
|
}
|
|
952
1511
|
}
|
|
953
|
-
return processOutput(output);
|
|
1512
|
+
return await processOutput(output);
|
|
954
1513
|
case 4:
|
|
955
1514
|
const chars = [];
|
|
956
1515
|
for (const char of realstr.slice(0, strcodes.code2).split('')) {
|
|
@@ -965,12 +1524,87 @@ SOFTWARE.
|
|
|
965
1524
|
}
|
|
966
1525
|
}
|
|
967
1526
|
}
|
|
968
|
-
return processOutput(output);
|
|
969
|
-
|
|
970
|
-
const decoded = characterEncodings(
|
|
1527
|
+
return await processOutput(output);
|
|
1528
|
+
case 5:
|
|
1529
|
+
const decoded = characterEncodings(strcodes.code2, realstr);
|
|
971
1530
|
if (decoded) {
|
|
972
|
-
return processOutput(decoded);
|
|
1531
|
+
return await processOutput(decoded);
|
|
1532
|
+
} else throw new Error('Invalid compressed string');
|
|
1533
|
+
case 7:
|
|
1534
|
+
const splitter = freqMapSplitters[binToDec(decToBin(strcodes.code2).slice(1))];
|
|
1535
|
+
output = freqMap.decompress(realstr, splitter);
|
|
1536
|
+
if (parseInt(decToBin(strcodes.code2).slice(0,1)) == 1) output = output.slice(0,-1);
|
|
1537
|
+
return await processOutput(output);
|
|
1538
|
+
case 8: {
|
|
1539
|
+
let bytes = [];
|
|
1540
|
+
for (const ch of realstr) {
|
|
1541
|
+
const c = ch.charCodeAt(0);
|
|
1542
|
+
bytes.push((c >> 8) & 0xFF, c & 0xFF);
|
|
1543
|
+
}
|
|
1544
|
+
if (strcodes.sequences) bytes.pop();
|
|
1545
|
+
|
|
1546
|
+
let out = '';
|
|
1547
|
+
for (const b of bytes) {
|
|
1548
|
+
out += String.fromCharCode(b);
|
|
1549
|
+
}
|
|
1550
|
+
|
|
1551
|
+
/* percent restore if needed */
|
|
1552
|
+
if (strcodes.code2 & 1) {
|
|
1553
|
+
out = out.replace(
|
|
1554
|
+
/[\x00-\x20\x7F-\xFF]/g,
|
|
1555
|
+
c => '%' + c.charCodeAt(0).toString(16).padStart(2, '0').toUpperCase()
|
|
1556
|
+
);
|
|
1557
|
+
}
|
|
1558
|
+
|
|
1559
|
+
/* punycode restore */
|
|
1560
|
+
if (strcodes.code2 & 2 && typeof punycode !== 'undefined') {
|
|
1561
|
+
const u = new URL(out);
|
|
1562
|
+
u.hostname = punycode.toASCII(u.hostname);
|
|
1563
|
+
out = u.href;
|
|
1564
|
+
}
|
|
1565
|
+
|
|
1566
|
+
return await processOutput(out);}
|
|
1567
|
+
case 9: {
|
|
1568
|
+
let idx = 0;
|
|
1569
|
+
const segCount = strcodes.code2 < 15 ? strcodes.code2 + 2 : realstr.charCodeAt(idx++) + 2;
|
|
1570
|
+
let out = '';
|
|
1571
|
+
|
|
1572
|
+
for (let i = 0; i < segCount; i++) {
|
|
1573
|
+
const len = realstr.charCodeAt(idx++);
|
|
1574
|
+
const segmentCompressed = realstr.slice(idx);
|
|
1575
|
+
|
|
1576
|
+
const seg = (await decompress(
|
|
1577
|
+
segmentCompressed, true
|
|
1578
|
+
)).slice(0, len);
|
|
1579
|
+
|
|
1580
|
+
out += seg;
|
|
1581
|
+
idx += (await compress(seg, {segmentation: false, justc: strcodes.repeatAfter, recursivecompression: strcodes.sequences})).length;
|
|
973
1582
|
}
|
|
1583
|
+
|
|
1584
|
+
return await processOutput(out);}
|
|
1585
|
+
case 10:
|
|
1586
|
+
const sliceChar = strcodes.code2 == 15;
|
|
1587
|
+
const repeatCount = sliceChar ? realstr.charCodeAt(0) + 15 : strcodes.code2;
|
|
1588
|
+
if (sliceChar) realstr = realstr.slice(1);
|
|
1589
|
+
return await processOutput(realstr.repeat(repeatCount));
|
|
1590
|
+
case 31: {
|
|
1591
|
+
let out = realstr;
|
|
1592
|
+
const depth = strcodes.code2;
|
|
1593
|
+
|
|
1594
|
+
for (let i = 0; i < depth; i++) {
|
|
1595
|
+
const first = out.charCodeAt(0) - 32;
|
|
1596
|
+
const meta = cryptCharCode(first, true);
|
|
1597
|
+
|
|
1598
|
+
if (meta.code === 31) {
|
|
1599
|
+
throw new Error('Attempt to nested recursive compression');
|
|
1600
|
+
}
|
|
1601
|
+
|
|
1602
|
+
out = await decompress(out, true);
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
return out;
|
|
1606
|
+
}
|
|
1607
|
+
default:
|
|
974
1608
|
throw new Error('Invalid compressed string');
|
|
975
1609
|
}
|
|
976
1610
|
}
|