@@ -2320,7 +2320,6 @@ JSContext *JS_NewContextRaw(JSRuntime *rt)
2320
2320
JSContext *JS_NewContext(JSRuntime *rt)
2321
2321
{
2322
2322
JSContext *ctx;
2323
-
2324
2323
ctx = JS_NewContextRaw(rt);
2325
2324
if (!ctx)
2326
2325
return NULL;
@@ -2336,7 +2335,7 @@ JSContext *JS_NewContext(JSRuntime *rt)
2336
2335
JS_AddIntrinsicPromise(ctx);
2337
2336
JS_AddIntrinsicBigInt(ctx);
2338
2337
JS_AddIntrinsicWeakRef(ctx);
2339
-
2338
+ JS_AddIntrinsicBase64(ctx);
2340
2339
JS_AddPerformance(ctx);
2341
2340
2342
2341
return ctx;
@@ -4037,26 +4036,26 @@ JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len)
4037
4036
size_t len;
4038
4037
int kind;
4039
4038
4040
- if (buf_len <= 0) {
4039
+ if (unlikely( buf_len <= 0) ) {
4041
4040
return JS_AtomToString(ctx, JS_ATOM_empty_string);
4042
4041
}
4043
4042
/* Compute string kind and length: 7-bit, 8-bit, 16-bit, 16-bit UTF-16 */
4044
4043
kind = utf8_scan(buf, buf_len, &len);
4045
- if (len > JS_STRING_LEN_MAX)
4044
+ if (unlikely( len > JS_STRING_LEN_MAX) )
4046
4045
return JS_ThrowRangeError(ctx, "invalid string length");
4047
4046
4048
4047
switch (kind) {
4049
4048
case UTF8_PLAIN_ASCII:
4050
4049
str = js_alloc_string(ctx, len, 0);
4051
- if (!str)
4050
+ if (unlikely( !str) )
4052
4051
return JS_EXCEPTION;
4053
4052
memcpy(str8(str), buf, len);
4054
4053
str8(str)[len] = '\0';
4055
4054
break;
4056
4055
case UTF8_NON_ASCII:
4057
4056
/* buf contains non-ASCII code-points, but limited to 8-bit values */
4058
4057
str = js_alloc_string(ctx, len, 0);
4059
- if (!str)
4058
+ if (unlikely( !str) )
4060
4059
return JS_EXCEPTION;
4061
4060
utf8_decode_buf8(str8(str), len + 1, buf, buf_len);
4062
4061
break;
@@ -4065,7 +4064,7 @@ JSValue JS_NewStringLen(JSContext *ctx, const char *buf, size_t buf_len)
4065
4064
//if (kind & UTF8_HAS_ERRORS)
4066
4065
// return JS_ThrowRangeError(ctx, "invalid UTF-8 sequence");
4067
4066
str = js_alloc_string(ctx, len, 1);
4068
- if (!str)
4067
+ if (unlikely( !str) )
4069
4068
return JS_EXCEPTION;
4070
4069
utf8_decode_buf16(str16(str), len, buf, buf_len);
4071
4070
break;
@@ -4077,10 +4076,10 @@ JSValue JS_NewTwoByteString(JSContext *ctx, const uint16_t *buf, size_t len)
4077
4076
{
4078
4077
JSString *str;
4079
4078
4080
- if (!len)
4079
+ if (unlikely( !len) )
4081
4080
return JS_AtomToString(ctx, JS_ATOM_empty_string);
4082
4081
str = js_alloc_string(ctx, len, 1);
4083
- if (!str)
4082
+ if (unlikely( !str) )
4084
4083
return JS_EXCEPTION;
4085
4084
memcpy(str16(str), buf, len * sizeof(*buf));
4086
4085
return JS_MKPTR(JS_TAG_STRING, str);
@@ -57764,6 +57763,295 @@ static void _JS_AddIntrinsicCallSite(JSContext *ctx)
57764
57763
countof(js_callsite_proto_funcs));
57765
57764
}
57766
57765
57766
+ static const unsigned char B64_ENC[64] = {
57767
+ 'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
57768
+ 'Q','R','S','T','U','V','W','X','Y','Z',
57769
+ 'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p',
57770
+ 'q','r','s','t','u','v','w','x','y','z',
57771
+ '0','1','2','3','4','5','6','7','8','9',
57772
+ '-','_'
57773
+ };
57774
+
57775
+ // Bit flags (0 means "invalid")
57776
+ enum { K_VAL = 1u, K_WS = 2u, K_PAD = 4u };
57777
+
57778
+ // sextet values for valid chars (others don't matter)
57779
+ static const uint8_t B64_VAL[256] = {
57780
+ ['A']=0, ['B']=1, ['C']=2, ['D']=3, ['E']=4, ['F']=5, ['G']=6, ['H']=7,
57781
+ ['I']=8, ['J']=9, ['K']=10,['L']=11,['M']=12,['N']=13,['O']=14,['P']=15,
57782
+ ['Q']=16,['R']=17,['S']=18,['T']=19,['U']=20,['V']=21,['W']=22,['X']=23,['Y']=24,['Z']=25,
57783
+ ['a']=26,['b']=27,['c']=28,['d']=29,['e']=30,['f']=31,['g']=32,['h']=33,
57784
+ ['i']=34,['j']=35,['k']=36,['l']=37,['m']=38,['n']=39,['o']=40,['p']=41,
57785
+ ['q']=42,['r']=43,['s']=44,['t']=45,['u']=46,['v']=47,['w']=48,['x']=49,['y']=50,['z']=51,
57786
+ ['0']=52,['1']=53,['2']=54,['3']=55,['4']=56,['5']=57,['6']=58,['7']=59,['8']=60,['9']=61,
57787
+ ['-']=62, ['_']=63, // base64url; swap to '+'/'/' if using standard base64
57788
+ };
57789
+
57790
+ // flag bitset: only non-zero entries listed; unspecified default to 0 (invalid)
57791
+ static const uint8_t B64_FLAGS[256] = {
57792
+ // whitespace
57793
+ [' ']=K_WS, ['\t']=K_WS, ['\r']=K_WS, ['\n']=K_WS,
57794
+ // padding
57795
+ ['=']=K_PAD,
57796
+ // valid chars
57797
+ ['A']=K_VAL,['B']=K_VAL,['C']=K_VAL,['D']=K_VAL,['E']=K_VAL,['F']=K_VAL,['G']=K_VAL,['H']=K_VAL,
57798
+ ['I']=K_VAL,['J']=K_VAL,['K']=K_VAL,['L']=K_VAL,['M']=K_VAL,['N']=K_VAL,['O']=K_VAL,['P']=K_VAL,
57799
+ ['Q']=K_VAL,['R']=K_VAL,['S']=K_VAL,['T']=K_VAL,['U']=K_VAL,['V']=K_VAL,['W']=K_VAL,['X']=K_VAL,
57800
+ ['Y']=K_VAL,['Z']=K_VAL,
57801
+ ['a']=K_VAL,['b']=K_VAL,['c']=K_VAL,['d']=K_VAL,['e']=K_VAL,['f']=K_VAL,['g']=K_VAL,['h']=K_VAL,
57802
+ ['i']=K_VAL,['j']=K_VAL,['k']=K_VAL,['l']=K_VAL,['m']=K_VAL,['n']=K_VAL,['o']=K_VAL,['p']=K_VAL,
57803
+ ['q']=K_VAL,['r']=K_VAL,['s']=K_VAL,['t']=K_VAL,['u']=K_VAL,['v']=K_VAL,['w']=K_VAL,['x']=K_VAL,
57804
+ ['y']=K_VAL,['z']=K_VAL,
57805
+ ['0']=K_VAL,['1']=K_VAL,['2']=K_VAL,['3']=K_VAL,['4']=K_VAL,['5']=K_VAL,['6']=K_VAL,['7']=K_VAL,
57806
+ ['8']=K_VAL,['9']=K_VAL,
57807
+ ['-']=K_VAL,['_']=K_VAL
57808
+ };
57809
+
57810
+ // ---------- encode (scalar with pair LUT) ----------
57811
+ static uint16_t B64_PAIR_LUT[4096];
57812
+ static int B64_PAIR_INIT = 0;
57813
+
57814
+ static inline void b64_pair_init_once(void) {
57815
+ if (unlikely(!B64_PAIR_INIT)) {
57816
+ for (int i=0;i<4096;i++) {
57817
+ uint8_t a = (uint8_t)(i >> 6);
57818
+ uint8_t b = (uint8_t)(i & 63);
57819
+ B64_PAIR_LUT[i] = (uint16_t)(B64_ENC[a] | (B64_ENC[b] << 8));
57820
+ }
57821
+ B64_PAIR_INIT = 1;
57822
+ }
57823
+ }
57824
+
57825
+ static inline size_t b64_encode(const uint8_t *src, size_t len, char *dst) {
57826
+ b64_pair_init_once();
57827
+ size_t i=0, j=0;
57828
+ size_t main = (len/3)*3;
57829
+ for (; i<main; i+=3, j+=4) {
57830
+ uint32_t v = (src[i]<<16)|(src[i+1]<<8)|src[i+2];
57831
+ uint16_t p0 = B64_PAIR_LUT[(v>>12)&0xFFF];
57832
+ uint16_t p1 = B64_PAIR_LUT[v & 0xFFF];
57833
+ memcpy(dst+j, &p0, 2);
57834
+ memcpy(dst+j+2, &p1, 2);
57835
+ }
57836
+ size_t rem = len-i;
57837
+ if (unlikely(rem==1)) {
57838
+ uint32_t v = src[i]<<16;
57839
+ dst[j++] = B64_ENC[(v>>18)&63];
57840
+ dst[j++] = B64_ENC[(v>>12)&63];
57841
+ dst[j++] = '=';
57842
+ dst[j++] = '=';
57843
+ } else if (unlikely(rem==2)) {
57844
+ uint32_t v = (src[i]<<16)|(src[i+1]<<8);
57845
+ dst[j++] = B64_ENC[(v>>18)&63];
57846
+ dst[j++] = B64_ENC[(v>>12)&63];
57847
+ dst[j++] = B64_ENC[(v>>6)&63];
57848
+ dst[j++] = '=';
57849
+ }
57850
+ return j;
57851
+ }
57852
+ static inline size_t
57853
+ b64_decode(const char *src, size_t len, uint8_t *dst, int *err)
57854
+ {
57855
+ uint32_t acc = 0;
57856
+ int bits = 0;
57857
+ size_t j = 0;
57858
+ int seen_pad = 0;
57859
+
57860
+ if (unlikely(err)) *err = 0;
57861
+
57862
+ for (size_t i=0; i<len; i++) {
57863
+ unsigned ch = (unsigned char)src[i];
57864
+ uint8_t flag = B64_FLAGS[ch];
57865
+
57866
+ if (likely(flag & K_VAL)) {
57867
+ // normal sextet
57868
+ if (unlikely(seen_pad)) { if (err) *err = 1; return 0; }
57869
+ acc = (acc << 6) | B64_VAL[ch];
57870
+ bits += 6;
57871
+ if (bits >= 8) {
57872
+ bits -= 8;
57873
+ dst[j++] = (uint8_t)((acc >> bits) & 0xFF);
57874
+ }
57875
+ } else if (flag & K_WS) {
57876
+ // whitespace -> skip
57877
+ continue;
57878
+ } else if (flag & K_PAD) {
57879
+ // '=' padding
57880
+ seen_pad = 1;
57881
+ // After '=', only ws or '=' is valid
57882
+ // Validate remaining input
57883
+ for (size_t k=i+1; k<len; k++) {
57884
+ unsigned ch2 = (unsigned char)src[k];
57885
+ uint8_t f2 = B64_FLAGS[ch2];
57886
+ if (f2 & K_WS) continue; // ws
57887
+ if (ch2!='=') { if (err) *err = 1; return 0; }
57888
+ }
57889
+ break;
57890
+ } else {
57891
+ // invalid
57892
+ if (err) *err = 1;
57893
+ return 0;
57894
+ }
57895
+ }
57896
+
57897
+ // Leftover bits are only valid if 0–2 '=' pads handled it
57898
+ if (unlikely(bits >= 6)) {
57899
+ if (err) *err = 1;
57900
+ return 0;
57901
+ }
57902
+
57903
+ return j;
57904
+ }
57905
+
57906
+ static JSValue js_btoa(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
57907
+ {
57908
+ JSValue val = likely(JS_IsString(argv[0])) ? JS_DupValue(ctx, argv[0])
57909
+ : JS_ToString(ctx, argv[0]);
57910
+ if (unlikely(JS_IsException(val)))
57911
+ return JS_EXCEPTION;
57912
+
57913
+ JSString *s = JS_VALUE_GET_STRING(val);
57914
+ size_t len = (size_t)s->len;
57915
+
57916
+ const uint8_t *in8 = NULL;
57917
+ uint8_t *tmp = NULL;
57918
+
57919
+ if (likely(!s->is_wide_char)) {
57920
+ in8 = (const uint8_t *)str8(s);
57921
+ } else {
57922
+ const uint16_t *src = str16(s);
57923
+ tmp = js_malloc(ctx, len ? len : 1);
57924
+ if (unlikely(!tmp)) {
57925
+ JS_FreeValue(ctx, val);
57926
+ return JS_ThrowOutOfMemory(ctx);
57927
+ }
57928
+ for (size_t i = 0; i < len; i++) {
57929
+ uint32_t c = src[i];
57930
+ if (unlikely(c > 0xFF)) {
57931
+ js_free(ctx, tmp);
57932
+ JS_FreeValue(ctx, val);
57933
+ return JS_ThrowTypeError(ctx, "character out of range (>255) at %zu", i);
57934
+ }
57935
+ tmp[i] = (uint8_t)c;
57936
+ }
57937
+ in8 = tmp;
57938
+ }
57939
+
57940
+ if (unlikely(len > (SIZE_MAX - 2) / 3)) {
57941
+ if (tmp) js_free(ctx, tmp);
57942
+ JS_FreeValue(ctx, val);
57943
+ return JS_ThrowRangeError(ctx, "input too large");
57944
+ }
57945
+ size_t out_len = 4 * ((len + 2) / 3);
57946
+ if (unlikely(out_len > JS_STRING_LEN_MAX)) {
57947
+ if (tmp) js_free(ctx, tmp);
57948
+ JS_FreeValue(ctx, val);
57949
+ return JS_ThrowRangeError(ctx, "output too large");
57950
+ }
57951
+
57952
+ JSString *ostr = js_alloc_string(ctx, out_len, /*is_wide=*/0);
57953
+ if (unlikely(!ostr)) {
57954
+ if (tmp) js_free(ctx, tmp);
57955
+ JS_FreeValue(ctx, val);
57956
+ return JS_EXCEPTION;
57957
+ }
57958
+
57959
+ b64_encode(in8, len, (char *)str8(ostr));
57960
+ ostr->len = out_len; // ensure length matches
57961
+
57962
+ if (tmp) js_free(ctx, tmp);
57963
+ JS_FreeValue(ctx, val);
57964
+ return JS_MKPTR(JS_TAG_STRING, ostr);
57965
+ }
57966
+
57967
+ static JSValue js_atob(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
57968
+ {
57969
+ JSValue val = likely(JS_IsString(argv[0])) ? JS_DupValue(ctx, argv[0])
57970
+ : JS_ToString(ctx, argv[0]);
57971
+ if (unlikely(JS_IsException(val)))
57972
+ return JS_EXCEPTION;
57973
+
57974
+ JSString *s = JS_VALUE_GET_STRING(val);
57975
+ size_t slen = (size_t)s->len;
57976
+
57977
+ const uint8_t *in;
57978
+ uint8_t *tmp = NULL;
57979
+
57980
+ if (likely(!s->is_wide_char)) {
57981
+ const uint8_t *p = (const uint8_t *)str8(s);
57982
+ // validate ASCII fast path
57983
+ for (size_t i = 0; i < slen; i++) {
57984
+ if (unlikely(p[i] & 0x80)) {
57985
+ JS_FreeValue(ctx, val);
57986
+ return JS_ThrowTypeError(ctx, "non-ASCII input");
57987
+ }
57988
+ }
57989
+ in = p;
57990
+ } else {
57991
+ const uint16_t *src = str16(s);
57992
+ tmp = js_malloc(ctx, slen ? slen : 1);
57993
+ if (unlikely(!tmp)) {
57994
+ JS_FreeValue(ctx, val);
57995
+ return JS_ThrowOutOfMemory(ctx);
57996
+ }
57997
+ for (size_t i = 0; i < slen; i++) {
57998
+ if (unlikely(src[i] > 0x7F)) {
57999
+ js_free(ctx, tmp);
58000
+ JS_FreeValue(ctx, val);
58001
+ return JS_ThrowTypeError(ctx, "non-ASCII input at %zu", i);
58002
+ }
58003
+ tmp[i] = (uint8_t)src[i];
58004
+ }
58005
+ in = tmp;
58006
+ }
58007
+
58008
+ // Max decoded size: slen*3/4
58009
+ if (unlikely(slen > (SIZE_MAX / 3) * 4)) {
58010
+ if (tmp) js_free(ctx, tmp);
58011
+ JS_FreeValue(ctx, val);
58012
+ return JS_ThrowRangeError(ctx, "input too large");
58013
+ }
58014
+ size_t out_cap = (slen / 4) * 3 + 3; // safe bound
58015
+ if (unlikely(out_cap > JS_STRING_LEN_MAX)) {
58016
+ if (tmp) js_free(ctx, tmp);
58017
+ JS_FreeValue(ctx, val);
58018
+ return JS_ThrowRangeError(ctx, "output too large");
58019
+ }
58020
+
58021
+ JSString *ostr = js_alloc_string(ctx, out_cap, /*is_wide=*/0);
58022
+ if (unlikely(!ostr)) {
58023
+ if (tmp) js_free(ctx, tmp);
58024
+ JS_FreeValue(ctx, val);
58025
+ return JS_EXCEPTION;
58026
+ }
58027
+
58028
+ uint8_t *dst = (uint8_t *)str8(ostr);
58029
+ int err = 0;
58030
+ size_t out_len = b64_decode((const char *)in, slen, dst, &err);
58031
+
58032
+ if (unlikely(tmp)) js_free(ctx, tmp);
58033
+ JS_FreeValue(ctx, val);
58034
+
58035
+ if (unlikely(err)) {
58036
+ js_free(ctx, ostr);
58037
+ return JS_ThrowTypeError(ctx, "invalid base64 input");
58038
+ }
58039
+
58040
+ ostr->len = out_len;
58041
+ return JS_MKPTR(JS_TAG_STRING, ostr);
58042
+ }
58043
+
58044
+ static const JSCFunctionListEntry js_base64_funcs[] = {
58045
+ JS_CFUNC_DEF("btoa", 1, js_btoa),
58046
+ JS_CFUNC_DEF("atob", 1, js_atob)};
58047
+
58048
+ void JS_AddIntrinsicBase64(JSContext *ctx)
58049
+ {
58050
+ JSValue global = JS_GetGlobalObject(ctx);
58051
+ JS_SetPropertyFunctionList(ctx, global, js_base64_funcs, sizeof(js_base64_funcs) / sizeof(js_base64_funcs[0]));
58052
+ JS_FreeValue(ctx, global);
58053
+ }
58054
+
57767
58055
bool JS_DetectModule(const char *input, size_t input_len)
57768
58056
{
57769
58057
#ifndef QJS_DISABLE_PARSER
0 commit comments