@scriptc/runtime 0.0.23 → 0.0.24
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/package.json +1 -1
- package/src/scr_array.c +65 -0
- package/src/scr_async.c +286 -5
- package/src/scr_bytes.c +634 -4
- package/src/scr_bytes_io.c +4 -2
- package/src/scr_file_handle.c +491 -0
- package/src/scr_island.c +10 -0
- package/src/scr_json.c +51 -19
- package/src/scr_lib.c +680 -30
- package/src/scr_number.c +9 -0
- package/src/scr_regex.c +12 -1
- package/src/scr_runtime.h +101 -8
- package/src/scr_string.c +13 -2
- package/src/scr_text_decoder_data.h +7389 -0
- package/src/scr_util.c +984 -0
- package/src/scr_win_stats.h +23 -0
package/src/scr_bytes.c
CHANGED
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
* encoding conversions (utf8 with WHATWG replacement, hex, base64) match
|
|
8
8
|
* Node byte-for-byte — the differential corpus holds them to it. */
|
|
9
9
|
#include "scr_runtime.h"
|
|
10
|
+
#ifdef SCR_TEXT_DECODER_LEGACY
|
|
11
|
+
#include "scr_text_decoder_data.h"
|
|
12
|
+
#endif
|
|
10
13
|
|
|
11
14
|
#include <math.h>
|
|
12
15
|
#include <stdio.h>
|
|
@@ -669,6 +672,16 @@ ScrStr *scr_strdec_end(const ScrStr *enc, double pending) {
|
|
|
669
672
|
return scr_bytes_decode_utf8(pend, np);
|
|
670
673
|
}
|
|
671
674
|
|
|
675
|
+
static void scr_bytes_to_str_bounds(const ScrBytes *b, double start, double end,
|
|
676
|
+
size_t *s0_out, size_t *e0_out) {
|
|
677
|
+
size_t len = b->len;
|
|
678
|
+
size_t s0 = start <= 0 ? 0 : ((size_t)start > len ? len : (size_t)start);
|
|
679
|
+
size_t e0 = end <= 0 ? 0 : ((size_t)end > len ? len : (size_t)end);
|
|
680
|
+
if (e0 < s0) e0 = s0;
|
|
681
|
+
*s0_out = s0;
|
|
682
|
+
*e0_out = e0;
|
|
683
|
+
}
|
|
684
|
+
|
|
672
685
|
/* toString(enc, start, end): Node slices then decodes — start/end clamp
|
|
673
686
|
* to [0, len] with start > end collapsing to empty and negative ends
|
|
674
687
|
* clamping to 0 (Node's slice-then-decode; an OMITTED end never reaches
|
|
@@ -676,10 +689,8 @@ ScrStr *scr_strdec_end(const ScrStr *enc, double pending) {
|
|
|
676
689
|
* form). Delegates to the whole-buffer decoder
|
|
677
690
|
* through a stack view — no allocation, no rc traffic. */
|
|
678
691
|
ScrStr *scr_bytes_to_str_range(const ScrBytes *b, const ScrStr *enc, double start, double end) {
|
|
679
|
-
size_t
|
|
680
|
-
|
|
681
|
-
size_t e0 = end <= 0 ? 0 : ((size_t)end > len ? len : (size_t)end);
|
|
682
|
-
if (e0 < s0) e0 = s0;
|
|
692
|
+
size_t s0, e0;
|
|
693
|
+
scr_bytes_to_str_bounds(b, start, end, &s0, &e0);
|
|
683
694
|
ScrBytes view = { .rc = 1, .len = e0 - s0, .elem = b->elem, .data = b->data + s0, .backing = NULL };
|
|
684
695
|
return scr_bytes_to_str(&view, enc);
|
|
685
696
|
}
|
|
@@ -733,6 +744,559 @@ static size_t scr_bytes_put_cp(char *out, size_t o, uint32_t cp) {
|
|
|
733
744
|
return o;
|
|
734
745
|
}
|
|
735
746
|
|
|
747
|
+
/* ── WHATWG/Node TextDecoder legacy encodings ──────────────────────────
|
|
748
|
+
*
|
|
749
|
+
* The frontend only calls this entry with a compile-time encoding id. The
|
|
750
|
+
* lookup data is generated from the repository's pinned Node 24 oracle, so
|
|
751
|
+
* the resulting binary has no ICU/iconv dependency and behaves identically
|
|
752
|
+
* on Darwin, Linux, and Windows. UTF-8 keeps its older dedicated entry above:
|
|
753
|
+
* programs using only the default decoder do not retain these legacy tables.
|
|
754
|
+
*/
|
|
755
|
+
#ifdef SCR_TEXT_DECODER_LEGACY
|
|
756
|
+
|
|
757
|
+
enum {
|
|
758
|
+
SCR_TD_X_USER_DEFINED = SCR_TD_SINGLE_COUNT,
|
|
759
|
+
SCR_TD_UTF16LE,
|
|
760
|
+
SCR_TD_UTF16BE,
|
|
761
|
+
SCR_TD_GB18030,
|
|
762
|
+
SCR_TD_BIG5,
|
|
763
|
+
SCR_TD_EUC_JP,
|
|
764
|
+
SCR_TD_ISO_2022_JP,
|
|
765
|
+
SCR_TD_SHIFT_JIS,
|
|
766
|
+
SCR_TD_EUC_KR,
|
|
767
|
+
};
|
|
768
|
+
|
|
769
|
+
typedef struct {
|
|
770
|
+
char *data;
|
|
771
|
+
size_t len;
|
|
772
|
+
} ScrTdOut;
|
|
773
|
+
|
|
774
|
+
static ScrTdOut scr_td_out_new(size_t input_len) {
|
|
775
|
+
if (input_len > (SIZE_MAX - 8) / 3) scr_bytes_oom();
|
|
776
|
+
ScrTdOut out = { malloc(input_len * 3 + 8), 0 };
|
|
777
|
+
if (!out.data) scr_bytes_oom();
|
|
778
|
+
return out;
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
static void scr_td_put(ScrTdOut *out, uint32_t cp) {
|
|
782
|
+
out->len = scr_bytes_put_cp(out->data, out->len, cp);
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
static void scr_td_error(ScrTdOut *out) {
|
|
786
|
+
scr_td_put(out, 0xfffd);
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
static ScrStr *scr_td_finish(ScrTdOut *out) {
|
|
790
|
+
ScrStr *str = scr_str_new(out->data, out->len);
|
|
791
|
+
free(out->data);
|
|
792
|
+
return str;
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
static ScrStr *scr_td_single_byte(const ScrBytes *b, unsigned encoding) {
|
|
796
|
+
ScrTdOut out = scr_td_out_new(b->len);
|
|
797
|
+
for (size_t i = 0; i < b->len; i++) {
|
|
798
|
+
uint8_t byte = b->data[i];
|
|
799
|
+
scr_td_put(&out, byte < 0x80 ? byte : scr_td_single[encoding][byte - 0x80]);
|
|
800
|
+
}
|
|
801
|
+
return scr_td_finish(&out);
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
static ScrStr *scr_td_x_user_defined(const ScrBytes *b) {
|
|
805
|
+
ScrTdOut out = scr_td_out_new(b->len);
|
|
806
|
+
for (size_t i = 0; i < b->len; i++) {
|
|
807
|
+
uint8_t byte = b->data[i];
|
|
808
|
+
scr_td_put(&out, byte < 0x80 ? byte : 0xf780 + byte - 0x80);
|
|
809
|
+
}
|
|
810
|
+
return scr_td_finish(&out);
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
static ScrStr *scr_td_utf16(const ScrBytes *b, bool be) {
|
|
814
|
+
const uint8_t *in = b->data;
|
|
815
|
+
size_t n = b->len;
|
|
816
|
+
/* TextDecoder's BOM handling strips only the BOM matching the selected
|
|
817
|
+
* endian decoder. The opposite BOM decodes to U+FFFE and remains. */
|
|
818
|
+
if (n >= 2 && ((!be && in[0] == 0xff && in[1] == 0xfe) ||
|
|
819
|
+
(be && in[0] == 0xfe && in[1] == 0xff))) {
|
|
820
|
+
in += 2;
|
|
821
|
+
n -= 2;
|
|
822
|
+
}
|
|
823
|
+
ScrTdOut out = scr_td_out_new(n);
|
|
824
|
+
size_t i = 0;
|
|
825
|
+
while (i + 1 < n) {
|
|
826
|
+
uint32_t cu = be ? ((uint32_t)in[i] << 8) | in[i + 1]
|
|
827
|
+
: (uint32_t)in[i] | ((uint32_t)in[i + 1] << 8);
|
|
828
|
+
i += 2;
|
|
829
|
+
if (cu >= 0xd800 && cu <= 0xdbff) {
|
|
830
|
+
if (i + 1 < n) {
|
|
831
|
+
uint32_t lo = be ? ((uint32_t)in[i] << 8) | in[i + 1]
|
|
832
|
+
: (uint32_t)in[i] | ((uint32_t)in[i + 1] << 8);
|
|
833
|
+
if (lo >= 0xdc00 && lo <= 0xdfff) {
|
|
834
|
+
i += 2;
|
|
835
|
+
scr_td_put(&out, 0x10000 + ((cu - 0xd800) << 10) + (lo - 0xdc00));
|
|
836
|
+
continue;
|
|
837
|
+
}
|
|
838
|
+
} else if (i < n) {
|
|
839
|
+
/* ICU treats a lead surrogate plus one final byte as one malformed
|
|
840
|
+
* UTF-16 subsequence. Consume that byte here so the odd-tail path
|
|
841
|
+
* below does not emit a second replacement. */
|
|
842
|
+
i++;
|
|
843
|
+
}
|
|
844
|
+
scr_td_error(&out);
|
|
845
|
+
continue;
|
|
846
|
+
}
|
|
847
|
+
if (cu >= 0xdc00 && cu <= 0xdfff) scr_td_error(&out);
|
|
848
|
+
else scr_td_put(&out, cu);
|
|
849
|
+
}
|
|
850
|
+
if (i < n) scr_td_error(&out); /* odd trailing byte */
|
|
851
|
+
return scr_td_finish(&out);
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
static uint32_t scr_td_gb_range(uint32_t pointer) {
|
|
855
|
+
size_t lo = 0;
|
|
856
|
+
size_t hi = sizeof scr_td_gb_ranges / sizeof scr_td_gb_ranges[0];
|
|
857
|
+
while (lo < hi) {
|
|
858
|
+
size_t mid = lo + (hi - lo) / 2;
|
|
859
|
+
const ScrTdGbRange *range = &scr_td_gb_ranges[mid];
|
|
860
|
+
if (pointer < range->start) hi = mid;
|
|
861
|
+
else if (pointer > range->end) lo = mid + 1;
|
|
862
|
+
else return range->code_point + pointer - range->start;
|
|
863
|
+
}
|
|
864
|
+
return 0;
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
/* A tiny prepend stack models the Encoding Standard's I/O queue restore.
|
|
868
|
+
* The gb18030 decoder is the only legacy decoder which can restore three
|
|
869
|
+
* bytes after discovering a malformed four-byte sequence. */
|
|
870
|
+
static ScrStr *scr_td_gb18030_decode(const ScrBytes *b) {
|
|
871
|
+
ScrTdOut out = scr_td_out_new(b->len);
|
|
872
|
+
uint8_t first = 0, second = 0, third = 0;
|
|
873
|
+
uint8_t replay[3];
|
|
874
|
+
size_t replay_len = 0;
|
|
875
|
+
size_t i = 0;
|
|
876
|
+
while (i < b->len || replay_len > 0) {
|
|
877
|
+
uint8_t byte = replay_len ? replay[--replay_len] : b->data[i++];
|
|
878
|
+
if (third) {
|
|
879
|
+
uint32_t cp = 0;
|
|
880
|
+
bool valid_fourth = byte >= 0x30 && byte <= 0x39;
|
|
881
|
+
if (valid_fourth) {
|
|
882
|
+
uint32_t pointer = (((uint32_t)(first - 0x81) * 10 + second - 0x30) * 126 +
|
|
883
|
+
third - 0x81) * 10 + byte - 0x30;
|
|
884
|
+
cp = scr_td_gb_range(pointer);
|
|
885
|
+
}
|
|
886
|
+
uint8_t old_second = second, old_third = third;
|
|
887
|
+
first = second = third = 0;
|
|
888
|
+
if (cp) {
|
|
889
|
+
scr_td_put(&out, cp);
|
|
890
|
+
} else {
|
|
891
|
+
scr_td_error(&out);
|
|
892
|
+
/* A structurally valid four-byte sequence with an unmapped pointer
|
|
893
|
+
* is consumed as one error. Only a malformed fourth byte restores
|
|
894
|
+
* the preceding payload bytes to Node's input queue. */
|
|
895
|
+
if (!valid_fourth) {
|
|
896
|
+
replay[replay_len++] = byte;
|
|
897
|
+
replay[replay_len++] = old_third;
|
|
898
|
+
replay[replay_len++] = old_second;
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
continue;
|
|
902
|
+
}
|
|
903
|
+
if (second) {
|
|
904
|
+
if (byte >= 0x81 && byte <= 0xfe) {
|
|
905
|
+
third = byte;
|
|
906
|
+
} else {
|
|
907
|
+
uint8_t old_second = second;
|
|
908
|
+
first = second = 0;
|
|
909
|
+
scr_td_error(&out);
|
|
910
|
+
replay[replay_len++] = byte;
|
|
911
|
+
replay[replay_len++] = old_second;
|
|
912
|
+
}
|
|
913
|
+
continue;
|
|
914
|
+
}
|
|
915
|
+
if (first) {
|
|
916
|
+
if (byte >= 0x30 && byte <= 0x39) {
|
|
917
|
+
second = byte;
|
|
918
|
+
continue;
|
|
919
|
+
}
|
|
920
|
+
uint8_t lead = first;
|
|
921
|
+
first = 0;
|
|
922
|
+
uint32_t cp = 0;
|
|
923
|
+
bool valid_trail = (byte >= 0x40 && byte <= 0x7e) ||
|
|
924
|
+
(byte >= 0x80 && byte <= 0xfe);
|
|
925
|
+
if (valid_trail) {
|
|
926
|
+
unsigned offset = byte < 0x7f ? 0x40 : 0x41;
|
|
927
|
+
cp = scr_td_gb18030[(lead - 0x81) * 190 + byte - offset];
|
|
928
|
+
}
|
|
929
|
+
if (cp) scr_td_put(&out, cp);
|
|
930
|
+
else {
|
|
931
|
+
scr_td_error(&out);
|
|
932
|
+
/* Non-ASCII invalid trails are consumed; ASCII is restored. */
|
|
933
|
+
if (!valid_trail && byte < 0x80) replay[replay_len++] = byte;
|
|
934
|
+
}
|
|
935
|
+
continue;
|
|
936
|
+
}
|
|
937
|
+
if (byte < 0x80) scr_td_put(&out, byte);
|
|
938
|
+
else if (byte == 0x80) scr_td_put(&out, 0x20ac);
|
|
939
|
+
else if (byte >= 0x81 && byte <= 0xfe) first = byte;
|
|
940
|
+
else scr_td_error(&out);
|
|
941
|
+
}
|
|
942
|
+
if (first || second || third) scr_td_error(&out);
|
|
943
|
+
return scr_td_finish(&out);
|
|
944
|
+
}
|
|
945
|
+
|
|
946
|
+
static ScrStr *scr_td_big5_decode(const ScrBytes *b) {
|
|
947
|
+
ScrTdOut out = scr_td_out_new(b->len);
|
|
948
|
+
uint8_t lead = 0;
|
|
949
|
+
for (size_t i = 0; i < b->len; i++) {
|
|
950
|
+
uint8_t byte = b->data[i];
|
|
951
|
+
if (lead) {
|
|
952
|
+
uint32_t cp = 0;
|
|
953
|
+
if ((byte >= 0x40 && byte <= 0x7e) || (byte >= 0xa1 && byte <= 0xfe)) {
|
|
954
|
+
unsigned offset = byte < 0x7f ? 0x40 : 0x62;
|
|
955
|
+
cp = scr_td_big5[(lead - 0x81) * 157 + byte - offset];
|
|
956
|
+
}
|
|
957
|
+
lead = 0;
|
|
958
|
+
if (cp) scr_td_put(&out, cp);
|
|
959
|
+
else {
|
|
960
|
+
scr_td_error(&out);
|
|
961
|
+
/* 0xff has a standalone ICU/Node mapping and is restored too. */
|
|
962
|
+
if (byte < 0x80 || byte == 0xff) i--;
|
|
963
|
+
}
|
|
964
|
+
continue;
|
|
965
|
+
}
|
|
966
|
+
if (byte < 0x80 || byte == 0x80) scr_td_put(&out, byte);
|
|
967
|
+
else if (byte >= 0x81 && byte <= 0xfe) lead = byte;
|
|
968
|
+
else if (byte == 0xff) scr_td_put(&out, 0xf8f8); /* ICU/Node mapping */
|
|
969
|
+
else scr_td_error(&out);
|
|
970
|
+
}
|
|
971
|
+
if (lead) scr_td_error(&out);
|
|
972
|
+
return scr_td_finish(&out);
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
static ScrStr *scr_td_euc_jp_decode(const ScrBytes *b) {
|
|
976
|
+
ScrTdOut out = scr_td_out_new(b->len);
|
|
977
|
+
uint8_t lead = 0;
|
|
978
|
+
bool jis0212 = false;
|
|
979
|
+
for (size_t i = 0; i < b->len; i++) {
|
|
980
|
+
uint8_t byte = b->data[i];
|
|
981
|
+
if (lead == 0x8e && byte >= 0xa1 && byte <= 0xdf) {
|
|
982
|
+
lead = 0;
|
|
983
|
+
scr_td_put(&out, 0xff61 + byte - 0xa1);
|
|
984
|
+
continue;
|
|
985
|
+
}
|
|
986
|
+
/* Node's pinned ICU EUC-JP converter exposes three NEC extensions just
|
|
987
|
+
* above the half-width katakana trail range. */
|
|
988
|
+
if (lead == 0x8e && byte >= 0xe0 && byte <= 0xe2) {
|
|
989
|
+
static const uint16_t extensions[] = { 0x00a2, 0x00a3, 0x00ac };
|
|
990
|
+
lead = 0;
|
|
991
|
+
scr_td_put(&out, extensions[byte - 0xe0]);
|
|
992
|
+
continue;
|
|
993
|
+
}
|
|
994
|
+
if (lead == 0x8f && byte >= 0xa1 && byte <= 0xfe) {
|
|
995
|
+
jis0212 = true;
|
|
996
|
+
lead = byte;
|
|
997
|
+
continue;
|
|
998
|
+
}
|
|
999
|
+
if (lead) {
|
|
1000
|
+
uint8_t old_lead = lead;
|
|
1001
|
+
lead = 0;
|
|
1002
|
+
uint32_t cp = 0;
|
|
1003
|
+
bool valid_trail = old_lead >= 0xa1 && old_lead <= 0xfe &&
|
|
1004
|
+
byte >= 0xa1 && byte <= 0xfe;
|
|
1005
|
+
if (valid_trail) {
|
|
1006
|
+
unsigned pointer = (old_lead - 0xa1) * 94 + byte - 0xa1;
|
|
1007
|
+
cp = jis0212 ? scr_td_jis0212[pointer] : scr_td_jis0208[pointer];
|
|
1008
|
+
}
|
|
1009
|
+
/* When the third byte of an 0x8f JIS-0212 sequence is malformed,
|
|
1010
|
+
* ICU reports the prefix and restores both following bytes. Model the
|
|
1011
|
+
* saved second byte as the next ordinary EUC-JP lead. */
|
|
1012
|
+
if (jis0212 && !valid_trail) {
|
|
1013
|
+
jis0212 = false;
|
|
1014
|
+
lead = old_lead;
|
|
1015
|
+
scr_td_error(&out);
|
|
1016
|
+
i--;
|
|
1017
|
+
continue;
|
|
1018
|
+
}
|
|
1019
|
+
jis0212 = false;
|
|
1020
|
+
if (cp) scr_td_put(&out, cp);
|
|
1021
|
+
else {
|
|
1022
|
+
scr_td_error(&out);
|
|
1023
|
+
/* C0/C1 bytes are restored; 0xa0 and 0xff are consumed with the
|
|
1024
|
+
* malformed sequence rather than producing a second error. The
|
|
1025
|
+
* 0x8e extension converter additionally restores 0xe5..0xfe. */
|
|
1026
|
+
if (byte < 0xa0 || (old_lead == 0x8e && byte >= 0xe5 && byte <= 0xfe)) i--;
|
|
1027
|
+
}
|
|
1028
|
+
continue;
|
|
1029
|
+
}
|
|
1030
|
+
if (byte < 0xa0 && byte != 0x8e && byte != 0x8f) scr_td_put(&out, byte);
|
|
1031
|
+
else if (byte == 0x8e || byte == 0x8f || (byte >= 0xa1 && byte <= 0xfe)) lead = byte;
|
|
1032
|
+
else scr_td_error(&out);
|
|
1033
|
+
}
|
|
1034
|
+
if (lead) scr_td_error(&out);
|
|
1035
|
+
return scr_td_finish(&out);
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
static ScrStr *scr_td_shift_jis_decode(const ScrBytes *b) {
|
|
1039
|
+
ScrTdOut out = scr_td_out_new(b->len);
|
|
1040
|
+
uint8_t lead = 0;
|
|
1041
|
+
for (size_t i = 0; i < b->len; i++) {
|
|
1042
|
+
uint8_t byte = b->data[i];
|
|
1043
|
+
if (lead) {
|
|
1044
|
+
uint32_t cp = 0;
|
|
1045
|
+
bool valid_trail = (byte >= 0x40 && byte <= 0x7e) ||
|
|
1046
|
+
(byte >= 0x80 && byte <= 0xfc);
|
|
1047
|
+
if (valid_trail) {
|
|
1048
|
+
unsigned offset = byte < 0x7f ? 0x40 : 0x41;
|
|
1049
|
+
unsigned lead_offset = lead < 0xa0 ? 0x81 : 0xc1;
|
|
1050
|
+
unsigned pointer = (lead - lead_offset) * 188 + byte - offset;
|
|
1051
|
+
cp = scr_td_shift_jis[pointer];
|
|
1052
|
+
}
|
|
1053
|
+
lead = 0;
|
|
1054
|
+
if (cp) scr_td_put(&out, cp);
|
|
1055
|
+
else {
|
|
1056
|
+
scr_td_error(&out);
|
|
1057
|
+
if (!valid_trail) i--;
|
|
1058
|
+
}
|
|
1059
|
+
continue;
|
|
1060
|
+
}
|
|
1061
|
+
/* ICU's ibm-943_P15A-2003 converter (Node's Shift_JIS backend) has
|
|
1062
|
+
* three historical C0/DEL swaps which its TextDecoder exposes. */
|
|
1063
|
+
if (byte == 0x1a) scr_td_put(&out, 0x1c);
|
|
1064
|
+
else if (byte == 0x1c) scr_td_put(&out, 0x7f);
|
|
1065
|
+
else if (byte == 0x7f) scr_td_put(&out, 0x1a);
|
|
1066
|
+
else if (byte < 0x80) scr_td_put(&out, byte);
|
|
1067
|
+
else if (byte >= 0xa1 && byte <= 0xdf) scr_td_put(&out, 0xff61 + byte - 0xa1);
|
|
1068
|
+
else if ((byte >= 0x81 && byte <= 0x9f) || (byte >= 0xe0 && byte <= 0xfc)) lead = byte;
|
|
1069
|
+
else scr_td_error(&out);
|
|
1070
|
+
}
|
|
1071
|
+
if (lead) scr_td_error(&out);
|
|
1072
|
+
return scr_td_finish(&out);
|
|
1073
|
+
}
|
|
1074
|
+
|
|
1075
|
+
static ScrStr *scr_td_euc_kr_decode(const ScrBytes *b) {
|
|
1076
|
+
ScrTdOut out = scr_td_out_new(b->len);
|
|
1077
|
+
uint8_t lead = 0;
|
|
1078
|
+
for (size_t i = 0; i < b->len; i++) {
|
|
1079
|
+
uint8_t byte = b->data[i];
|
|
1080
|
+
if (lead) {
|
|
1081
|
+
uint32_t cp = 0;
|
|
1082
|
+
if (lead >= 0xa1 && lead <= 0xfe && byte >= 0xa1 && byte <= 0xfe) {
|
|
1083
|
+
cp = scr_td_euc_kr[(lead - 0xa1) * 94 + byte - 0xa1];
|
|
1084
|
+
}
|
|
1085
|
+
lead = 0;
|
|
1086
|
+
if (cp) scr_td_put(&out, cp);
|
|
1087
|
+
else {
|
|
1088
|
+
scr_td_error(&out);
|
|
1089
|
+
if (byte < 0xa0) i--;
|
|
1090
|
+
}
|
|
1091
|
+
continue;
|
|
1092
|
+
}
|
|
1093
|
+
if (byte < 0xa0 && byte != 0x8e && byte != 0x8f) scr_td_put(&out, byte);
|
|
1094
|
+
else if (byte >= 0xa1 && byte <= 0xfe) lead = byte;
|
|
1095
|
+
else scr_td_error(&out);
|
|
1096
|
+
}
|
|
1097
|
+
if (lead) scr_td_error(&out);
|
|
1098
|
+
return scr_td_finish(&out);
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
enum ScrTdIsoState {
|
|
1102
|
+
SCR_TD_ISO_ASCII,
|
|
1103
|
+
SCR_TD_ISO_ROMAN,
|
|
1104
|
+
SCR_TD_ISO_KATAKANA,
|
|
1105
|
+
SCR_TD_ISO_LEAD,
|
|
1106
|
+
SCR_TD_ISO_TRAIL,
|
|
1107
|
+
SCR_TD_ISO_ESCAPE_START,
|
|
1108
|
+
SCR_TD_ISO_ESCAPE,
|
|
1109
|
+
};
|
|
1110
|
+
|
|
1111
|
+
static ScrStr *scr_td_iso_2022_jp_decode(const ScrBytes *b) {
|
|
1112
|
+
ScrTdOut out = scr_td_out_new(b->len);
|
|
1113
|
+
enum ScrTdIsoState state = SCR_TD_ISO_ASCII;
|
|
1114
|
+
enum ScrTdIsoState output_state = SCR_TD_ISO_ASCII;
|
|
1115
|
+
uint8_t lead = 0;
|
|
1116
|
+
int replay = -1;
|
|
1117
|
+
bool output_flag = false;
|
|
1118
|
+
size_t i = 0;
|
|
1119
|
+
bool at_eof = false;
|
|
1120
|
+
while (!at_eof) {
|
|
1121
|
+
int item;
|
|
1122
|
+
if (replay >= 0) {
|
|
1123
|
+
item = replay;
|
|
1124
|
+
replay = -1;
|
|
1125
|
+
} else {
|
|
1126
|
+
item = i < b->len ? b->data[i++] : -1;
|
|
1127
|
+
}
|
|
1128
|
+
uint8_t byte = item < 0 ? 0 : (uint8_t)item;
|
|
1129
|
+
/* ICU treats CR/LF as line boundaries while a non-ASCII designation is
|
|
1130
|
+
* active: emit the separator and resume in ASCII. A pending JIS lead is
|
|
1131
|
+
* different (TRAIL state below) — there the line break completes an
|
|
1132
|
+
* ill-formed pair and remains a replacement, matching Node. */
|
|
1133
|
+
if (item >= 0 && (byte == 0x0a || byte == 0x0d) &&
|
|
1134
|
+
(state == SCR_TD_ISO_KATAKANA || state == SCR_TD_ISO_LEAD)) {
|
|
1135
|
+
output_flag = false;
|
|
1136
|
+
state = output_state = SCR_TD_ISO_ASCII;
|
|
1137
|
+
scr_td_put(&out, byte);
|
|
1138
|
+
continue;
|
|
1139
|
+
}
|
|
1140
|
+
switch (state) {
|
|
1141
|
+
case SCR_TD_ISO_ASCII:
|
|
1142
|
+
case SCR_TD_ISO_ROMAN:
|
|
1143
|
+
case SCR_TD_ISO_KATAKANA:
|
|
1144
|
+
if (item < 0) { at_eof = true; break; }
|
|
1145
|
+
if (byte == 0x1b) { state = SCR_TD_ISO_ESCAPE_START; break; }
|
|
1146
|
+
if (state == SCR_TD_ISO_ROMAN && byte == 0x5c) {
|
|
1147
|
+
output_flag = false; scr_td_put(&out, 0x00a5); break;
|
|
1148
|
+
}
|
|
1149
|
+
if (state == SCR_TD_ISO_ROMAN && byte == 0x7e) {
|
|
1150
|
+
output_flag = false; scr_td_put(&out, 0x203e); break;
|
|
1151
|
+
}
|
|
1152
|
+
if (state == SCR_TD_ISO_KATAKANA && byte >= 0x21 && byte <= 0x5f) {
|
|
1153
|
+
output_flag = false; scr_td_put(&out, 0xff61 + byte - 0x21); break;
|
|
1154
|
+
}
|
|
1155
|
+
if (state != SCR_TD_ISO_KATAKANA && byte < 0x80 && byte != 0x0e && byte != 0x0f) {
|
|
1156
|
+
output_flag = false; scr_td_put(&out, byte); break;
|
|
1157
|
+
}
|
|
1158
|
+
output_flag = false; scr_td_error(&out); break;
|
|
1159
|
+
|
|
1160
|
+
case SCR_TD_ISO_LEAD:
|
|
1161
|
+
if (item < 0) { at_eof = true; break; }
|
|
1162
|
+
if (byte == 0x1b) { state = SCR_TD_ISO_ESCAPE_START; break; }
|
|
1163
|
+
if (byte >= 0x21 && byte <= 0x7e) {
|
|
1164
|
+
output_flag = false; lead = byte; state = SCR_TD_ISO_TRAIL; break;
|
|
1165
|
+
}
|
|
1166
|
+
/* ICU's fixed-width JIS converter folds two adjacent non-starter
|
|
1167
|
+
* bytes into one malformed subsequence. A valid lead, ESC, and the
|
|
1168
|
+
* SO/SI controls begin their own item and must remain queued; CR/LF
|
|
1169
|
+
* following an invalid byte is payload here rather than the line
|
|
1170
|
+
* reset handled above. */
|
|
1171
|
+
if (byte != 0x0e && byte != 0x0f && i < b->len) {
|
|
1172
|
+
uint8_t next = b->data[i];
|
|
1173
|
+
bool starts_item = next == 0x0e || next == 0x0f || next == 0x1b ||
|
|
1174
|
+
(next >= 0x21 && next <= 0x7e);
|
|
1175
|
+
if (!starts_item) i++;
|
|
1176
|
+
}
|
|
1177
|
+
output_flag = false; scr_td_error(&out); break;
|
|
1178
|
+
|
|
1179
|
+
case SCR_TD_ISO_TRAIL:
|
|
1180
|
+
if (item < 0) { state = SCR_TD_ISO_LEAD; scr_td_error(&out); at_eof = true; break; }
|
|
1181
|
+
if (byte == 0x1b) { state = SCR_TD_ISO_ESCAPE_START; scr_td_error(&out); break; }
|
|
1182
|
+
state = SCR_TD_ISO_LEAD;
|
|
1183
|
+
if (byte >= 0x21 && byte <= 0x7e) {
|
|
1184
|
+
uint32_t cp = scr_td_jis0208[(lead - 0x21) * 94 + byte - 0x21];
|
|
1185
|
+
if (cp) scr_td_put(&out, cp); else scr_td_error(&out);
|
|
1186
|
+
} else {
|
|
1187
|
+
scr_td_error(&out);
|
|
1188
|
+
/* SO/SI are standalone illegal items in ICU's JIS state rather
|
|
1189
|
+
* than trails consumed with the pending lead. Replay them so they
|
|
1190
|
+
* each contribute their own replacement. */
|
|
1191
|
+
if (byte == 0x0e || byte == 0x0f) i--;
|
|
1192
|
+
}
|
|
1193
|
+
break;
|
|
1194
|
+
|
|
1195
|
+
case SCR_TD_ISO_ESCAPE_START:
|
|
1196
|
+
if (item >= 0 && (byte == 0x24 || byte == 0x25 || byte == 0x26 ||
|
|
1197
|
+
byte == 0x28 || byte == 0x2e)) {
|
|
1198
|
+
lead = byte; state = SCR_TD_ISO_ESCAPE; break;
|
|
1199
|
+
}
|
|
1200
|
+
/* ICU recognizes ESC O as a complete but unsupported single-shift
|
|
1201
|
+
* escape, consuming the O with the replacement. */
|
|
1202
|
+
if (item >= 0 && byte == 0x4f) {
|
|
1203
|
+
output_flag = false; state = output_state; scr_td_error(&out); break;
|
|
1204
|
+
}
|
|
1205
|
+
if (item >= 0) i--;
|
|
1206
|
+
output_flag = false; state = output_state; scr_td_error(&out);
|
|
1207
|
+
if (item < 0) at_eof = true;
|
|
1208
|
+
break;
|
|
1209
|
+
|
|
1210
|
+
case SCR_TD_ISO_ESCAPE: {
|
|
1211
|
+
enum ScrTdIsoState next = (enum ScrTdIsoState)-1;
|
|
1212
|
+
if (item >= 0 && lead == 0x28 && byte == 0x42) next = SCR_TD_ISO_ASCII;
|
|
1213
|
+
else if (item >= 0 && lead == 0x28 && (byte == 0x48 || byte == 0x4a)) next = SCR_TD_ISO_ROMAN;
|
|
1214
|
+
else if (item >= 0 && lead == 0x28 && byte == 0x49) next = SCR_TD_ISO_KATAKANA;
|
|
1215
|
+
else if (item >= 0 && lead == 0x24 && (byte == 0x40 || byte == 0x42)) next = SCR_TD_ISO_LEAD;
|
|
1216
|
+
if ((int)next >= 0) {
|
|
1217
|
+
state = output_state = next;
|
|
1218
|
+
bool repeated = output_flag;
|
|
1219
|
+
output_flag = !repeated;
|
|
1220
|
+
if (repeated) scr_td_error(&out);
|
|
1221
|
+
break;
|
|
1222
|
+
}
|
|
1223
|
+
/* Some ICU designation families need one more final byte. A known
|
|
1224
|
+
* final consumes the whole four-byte escape as one error; an unknown
|
|
1225
|
+
* final restores both payload bytes and leaves that final queued. */
|
|
1226
|
+
bool extended_prefix =
|
|
1227
|
+
(lead == 0x24 && (byte == 0x28 || byte == 0x29 || byte == 0x2a || byte == 0x2b)) ||
|
|
1228
|
+
(lead == 0x25 && byte == 0x2f);
|
|
1229
|
+
if (extended_prefix) {
|
|
1230
|
+
if (i == b->len) {
|
|
1231
|
+
output_flag = false; state = output_state; scr_td_error(&out);
|
|
1232
|
+
break;
|
|
1233
|
+
}
|
|
1234
|
+
uint8_t final = b->data[i];
|
|
1235
|
+
bool known_final =
|
|
1236
|
+
(lead == 0x24 && byte == 0x28 &&
|
|
1237
|
+
((final >= 0x40 && final <= 0x45) || (final >= 0x47 && final <= 0x4d))) ||
|
|
1238
|
+
(lead == 0x24 && byte == 0x29 &&
|
|
1239
|
+
(final == 0x41 || final == 0x43 || final == 0x45 || final == 0x47)) ||
|
|
1240
|
+
(lead == 0x24 && byte == 0x2a && final == 0x48) ||
|
|
1241
|
+
(lead == 0x24 && byte == 0x2b && final >= 0x49 && final <= 0x4d) ||
|
|
1242
|
+
(lead == 0x25 && byte == 0x2f &&
|
|
1243
|
+
((final >= 0x40 && final <= 0x41) || (final >= 0x43 && final <= 0x46)));
|
|
1244
|
+
if (known_final) {
|
|
1245
|
+
i++;
|
|
1246
|
+
output_flag = false; state = output_state; scr_td_error(&out);
|
|
1247
|
+
break;
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
/* ICU consumes the complete unsupported designations it recognizes,
|
|
1251
|
+
* while other malformed payloads are restored to the input queue. */
|
|
1252
|
+
bool consume_error =
|
|
1253
|
+
(lead == 0x24 && byte == 0x41) ||
|
|
1254
|
+
(lead == 0x28 && ((byte >= 0x40 && byte <= 0x47) || byte == 0x4b || byte == 0x52)) ||
|
|
1255
|
+
(lead == 0x25 && byte == 0x42) ||
|
|
1256
|
+
(lead == 0x2e && (byte == 0x41 || byte == 0x46));
|
|
1257
|
+
if (item >= 0 && lead == 0x26 && byte == 0x40) {
|
|
1258
|
+
state = output_state = SCR_TD_ISO_LEAD;
|
|
1259
|
+
bool repeated = output_flag;
|
|
1260
|
+
output_flag = !repeated;
|
|
1261
|
+
if (repeated) scr_td_error(&out);
|
|
1262
|
+
break;
|
|
1263
|
+
}
|
|
1264
|
+
if (item < 0 || consume_error) {
|
|
1265
|
+
output_flag = false; state = output_state; scr_td_error(&out);
|
|
1266
|
+
if (item < 0) at_eof = true;
|
|
1267
|
+
break;
|
|
1268
|
+
}
|
|
1269
|
+
i--;
|
|
1270
|
+
/* The first escape payload byte is restored before the current
|
|
1271
|
+
* byte. Replay it through the full prior state machine: in JIS
|
|
1272
|
+
* mode it can become the lead paired with the current byte. */
|
|
1273
|
+
replay = lead;
|
|
1274
|
+
output_flag = false; state = output_state; scr_td_error(&out);
|
|
1275
|
+
break;
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
return scr_td_finish(&out);
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
ScrStr *scr_text_decode_legacy(const ScrBytes *b, double encoding_value) {
|
|
1283
|
+
unsigned encoding = (unsigned)encoding_value;
|
|
1284
|
+
if (encoding < SCR_TD_SINGLE_COUNT) return scr_td_single_byte(b, encoding);
|
|
1285
|
+
switch (encoding) {
|
|
1286
|
+
case SCR_TD_X_USER_DEFINED: return scr_td_x_user_defined(b);
|
|
1287
|
+
case SCR_TD_UTF16LE: return scr_td_utf16(b, false);
|
|
1288
|
+
case SCR_TD_UTF16BE: return scr_td_utf16(b, true);
|
|
1289
|
+
case SCR_TD_GB18030: return scr_td_gb18030_decode(b);
|
|
1290
|
+
case SCR_TD_BIG5: return scr_td_big5_decode(b);
|
|
1291
|
+
case SCR_TD_EUC_JP: return scr_td_euc_jp_decode(b);
|
|
1292
|
+
case SCR_TD_ISO_2022_JP: return scr_td_iso_2022_jp_decode(b);
|
|
1293
|
+
case SCR_TD_SHIFT_JIS: return scr_td_shift_jis_decode(b);
|
|
1294
|
+
case SCR_TD_EUC_KR: return scr_td_euc_kr_decode(b);
|
|
1295
|
+
default: return scr_str_new("", 0); /* compiler invariant */
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
#endif /* SCR_TEXT_DECODER_LEGACY */
|
|
1299
|
+
|
|
736
1300
|
/* Decode the next code point from an ScrStr's storage — ALWAYS valid
|
|
737
1301
|
* UTF-8 (the string runtime never stores ill-formed sequences), so no
|
|
738
1302
|
* validation re-runs here. */
|
|
@@ -831,6 +1395,72 @@ ScrStr *scr_bytes_to_str(const ScrBytes *b, const ScrStr *enc) {
|
|
|
831
1395
|
return scr_bytes_decode_utf8(in, n);
|
|
832
1396
|
}
|
|
833
1397
|
|
|
1398
|
+
/* Buffer.toString with a runtime-valued encoding. Literal call sites fold
|
|
1399
|
+
* this same alias table in the frontend; variables arrive here, where Node
|
|
1400
|
+
* also accepts ASCII case variants and rejects unknown names catchably. */
|
|
1401
|
+
static bool scr_enc_eq_ci(const ScrStr *raw, const char *name) {
|
|
1402
|
+
size_t n = strlen(name);
|
|
1403
|
+
if (raw->len != n) return false;
|
|
1404
|
+
for (size_t i = 0; i < n; i++) {
|
|
1405
|
+
char c = raw->data[i];
|
|
1406
|
+
if (c >= 'A' && c <= 'Z') c = (char)(c + ('a' - 'A'));
|
|
1407
|
+
if (c != name[i]) return false;
|
|
1408
|
+
}
|
|
1409
|
+
return true;
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
static ScrStr *scr_bytes_normalize_encoding(const ScrStr *raw) {
|
|
1413
|
+
static const struct { const char *from; const char *to; } map[] = {
|
|
1414
|
+
{"utf8", "utf8"}, {"utf-8", "utf8"}, {"hex", "hex"},
|
|
1415
|
+
{"base64", "base64"}, {"base64url", "base64url"},
|
|
1416
|
+
{"latin1", "latin1"}, {"binary", "latin1"}, {"ascii", "ascii"},
|
|
1417
|
+
{"utf16le", "utf16le"}, {"utf-16le", "utf16le"},
|
|
1418
|
+
{"ucs2", "utf16le"}, {"ucs-2", "utf16le"},
|
|
1419
|
+
};
|
|
1420
|
+
for (size_t i = 0; i < sizeof map / sizeof map[0]; i++) {
|
|
1421
|
+
if (scr_enc_eq_ci(raw, map[i].from)) {
|
|
1422
|
+
return scr_str_new(map[i].to, strlen(map[i].to));
|
|
1423
|
+
}
|
|
1424
|
+
}
|
|
1425
|
+
static const char prefix[] = "Unknown encoding: ";
|
|
1426
|
+
const size_t prefix_len = sizeof prefix - 1;
|
|
1427
|
+
if (raw->len > SIZE_MAX - prefix_len) scr_bytes_oom();
|
|
1428
|
+
const size_t msg_len = prefix_len + raw->len;
|
|
1429
|
+
char *msg = malloc(msg_len);
|
|
1430
|
+
if (!msg) scr_bytes_oom();
|
|
1431
|
+
memcpy(msg, prefix, prefix_len);
|
|
1432
|
+
memcpy(msg + prefix_len, raw->data, raw->len);
|
|
1433
|
+
scr_throw_error_msg_code(SCR_ERR_TYPE, msg, msg_len,
|
|
1434
|
+
"ERR_UNKNOWN_ENCODING");
|
|
1435
|
+
free(msg);
|
|
1436
|
+
return NULL;
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
ScrStr *scr_bytes_to_str_checked(const ScrBytes *b, const ScrStr *enc) {
|
|
1440
|
+
/* Node returns before resolving the encoding when there are no bytes
|
|
1441
|
+
* to decode, so even an unknown runtime name answers the empty string. */
|
|
1442
|
+
if (b->len == 0) return scr_str_new("", 0);
|
|
1443
|
+
ScrStr *normalized = scr_bytes_normalize_encoding(enc);
|
|
1444
|
+
if (!normalized) return NULL;
|
|
1445
|
+
ScrStr *out = scr_bytes_to_str(b, normalized);
|
|
1446
|
+
scr_str_release(normalized);
|
|
1447
|
+
return out;
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
ScrStr *scr_bytes_to_str_checked_range(const ScrBytes *b, const ScrStr *enc,
|
|
1451
|
+
double start, double end) {
|
|
1452
|
+
/* The range is selected before Node resolves the encoding. A clamped
|
|
1453
|
+
* empty window therefore answers "" even for an unknown name. */
|
|
1454
|
+
size_t s0, e0;
|
|
1455
|
+
scr_bytes_to_str_bounds(b, start, end, &s0, &e0);
|
|
1456
|
+
if (s0 == e0) return scr_str_new("", 0);
|
|
1457
|
+
ScrStr *normalized = scr_bytes_normalize_encoding(enc);
|
|
1458
|
+
if (!normalized) return NULL;
|
|
1459
|
+
ScrStr *out = scr_bytes_to_str_range(b, normalized, start, end);
|
|
1460
|
+
scr_str_release(normalized);
|
|
1461
|
+
return out;
|
|
1462
|
+
}
|
|
1463
|
+
|
|
834
1464
|
static int scr_hex_val(uint8_t c) {
|
|
835
1465
|
if (c >= '0' && c <= '9') return c - '0';
|
|
836
1466
|
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
|
package/src/scr_bytes_io.c
CHANGED
|
@@ -153,12 +153,14 @@ ScrBytes *scr_crypto_random_bytes(double n) {
|
|
|
153
153
|
|
|
154
154
|
/* ── process.stdout/stderr.write(buf) ──────────────────────────────────── */
|
|
155
155
|
|
|
156
|
-
bool scr_process_stdout_write_bytes(const ScrBytes *b) {
|
|
156
|
+
bool scr_process_stdout_write_bytes(const ScrBytes *b, const ScrStr *encoding) {
|
|
157
|
+
(void)encoding;
|
|
157
158
|
scr_stdio_write(1, b->data, b->len * scr_bytes_elem_size(b->elem));
|
|
158
159
|
return true;
|
|
159
160
|
}
|
|
160
161
|
|
|
161
|
-
bool scr_process_stderr_write_bytes(const ScrBytes *b) {
|
|
162
|
+
bool scr_process_stderr_write_bytes(const ScrBytes *b, const ScrStr *encoding) {
|
|
163
|
+
(void)encoding;
|
|
162
164
|
scr_stdio_write(2, b->data, b->len * scr_bytes_elem_size(b->elem));
|
|
163
165
|
return true;
|
|
164
166
|
}
|