@@ -2321,6 +2321,7 @@ JSContext *JS_NewContext(JSRuntime *rt)
23212321 JS_AddIntrinsicPromise(ctx);
23222322 JS_AddIntrinsicBigInt(ctx);
23232323 JS_AddIntrinsicWeakRef(ctx);
2324+ JS_AddIntrinsicBase64(ctx);
23242325
23252326 JS_AddPerformance(ctx);
23262327
@@ -57422,6 +57423,144 @@ static void insert_weakref_record(JSValueConst target,
5742257423 *pwr = wr;
5742357424}
5742457425
57426+ /* urlsafe_base64 atob/btoa */
57427+
57428+ // Base64 encoding table
57429+ static const char base64_table[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
57430+
57431+ // btoa: binary to ASCII (base64 encode)
57432+ char* btoa(JSContext *ctx, const char* bin, size_t len) {
57433+ if (bin == NULL || len == 0) return NULL;
57434+ // Calculate output length (including padding)
57435+ size_t out_len = 4 * ((len + 2) / 3) + 1;
57436+ // Allocate memory for output string (plus null terminator)
57437+ char* out = (char*)js_mallocz(ctx, out_len);
57438+
57439+ if (out == NULL) return NULL;
57440+
57441+ // Base64 encoding process
57442+ size_t i, j;
57443+
57444+ for (i = 0, j = 0; i < len; i += 3, j += 4) {
57445+ uint32_t triple = (bin[i] << 16);
57446+ if (i + 1 < len) triple |= (bin[i + 1] << 8);
57447+ if (i + 2 < len) triple |= bin[i + 2];
57448+
57449+ out[j] = base64_table[(triple >> 18) & 0x3F];
57450+ out[j + 1] = base64_table[(triple >> 12) & 0x3F];
57451+ out[j + 2] = (i + 1 < len) ? base64_table[(triple >> 6) & 0x3F] : '=';
57452+ out[j + 3] = (i + 2 < len) ? base64_table[triple & 0x3F] : '=';
57453+ }
57454+
57455+ out[out_len] = '\0';
57456+ return out;
57457+ }
57458+
57459+ static JSValue js_base64_btoa(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
57460+ const char *fmt_str = NULL;
57461+ const char *result_str = NULL;
57462+ JSValue v;
57463+
57464+ if (argc > 0) {
57465+ fmt_str = JS_ToCString(ctx, argv[0]);
57466+ result_str = btoa(ctx, fmt_str, strlen(fmt_str));
57467+ v = JS_NewString(ctx, result_str);
57468+ JS_FreeCString(ctx, fmt_str);
57469+ return v;
57470+ } else {
57471+ return JS_ThrowTypeError(ctx, "ERR_MISSING_ARGS");
57472+ }
57473+ }
57474+
57475+ static int base64_index(char c) {
57476+ if (c >= 'A' && c <= 'Z')
57477+ return c - 'A';
57478+ if (c >= 'a' && c <= 'z')
57479+ return c - 'a' + 26;
57480+ if (c >= '0' && c <= '9')
57481+ return c - '0' + 52;
57482+ if (c == '-')
57483+ return 62;
57484+ if (c == '_')
57485+ return 63;
57486+
57487+ return -1; // Invalid character
57488+ }
57489+
57490+ // atob: ASCII to binary (base64 decode)
57491+ char* atob(JSContext *ctx, const char* str) {
57492+ if (str == NULL) return NULL;
57493+
57494+ size_t str_len = strlen(str);
57495+
57496+ if (str_len % 4 != 0) return NULL; // Invalid base64 string
57497+
57498+ // Calculate output length
57499+ size_t len = str_len / 4 * 3;
57500+
57501+ if (str[str_len - 1] == '=') len--;
57502+
57503+ if (str[str_len - 2] == '=') len--;
57504+
57505+ // Allocate memory for output data
57506+ size_t out_len = len+1;
57507+ char* out = (char*)js_mallocz(ctx, out_len);
57508+
57509+ if (out == NULL) return NULL;
57510+
57511+ // Base64 decoding process
57512+ size_t i, j;
57513+
57514+ for (i = 0, j = 0; i < str_len; i += 4, j += 3) {
57515+ int a = base64_index(str[i]);
57516+ int b = base64_index(str[i + 1]);
57517+ int c = str[i + 2] == '=' ? 0 : base64_index(str[i + 2]);
57518+ int d = str[i + 3] == '=' ? 0 : base64_index(str[i + 3]);
57519+
57520+ if (a == -1 || b == -1 || c == -1 || d == -1) {
57521+ js_free(ctx, out);
57522+ return NULL; // Invalid character
57523+ }
57524+
57525+ uint32_t triple = (a << 18) | (b << 12) | (c << 6) | d;
57526+ out[j] = (triple >> 16) & 0xFF;
57527+
57528+ if (j + 1 < len) out[j + 1] = (triple >> 8) & 0xFF;
57529+ if (j + 2 < len) out[j + 2] = triple & 0xFF;
57530+ }
57531+
57532+ out[out_len] = '\0';
57533+ return out;
57534+ }
57535+
57536+
57537+ static JSValue js_base64_atob(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv) {
57538+ const char *fmt_str = NULL;
57539+ const char *result_str = NULL;
57540+ JSValue v;
57541+
57542+ if (argc > 0) {
57543+ fmt_str = JS_ToCString(ctx, argv[0]);
57544+ result_str = atob(ctx, fmt_str);
57545+ v = JS_NewString(ctx, result_str);
57546+ JS_FreeCString(ctx, fmt_str);
57547+ return v;
57548+ } else {
57549+ return JS_ThrowTypeError(ctx, "ERR_MISSING_ARGS");
57550+ }
57551+ }
57552+
57553+ static const JSCFunctionListEntry js_base64_funcs[] = {
57554+ JS_CFUNC_DEF("btoa", 1, js_base64_btoa ),
57555+ JS_CFUNC_DEF("atob", 1, js_base64_atob ),
57556+ };
57557+
57558+ void JS_AddIntrinsicBase64(JSContext *ctx)
57559+ {
57560+ JS_SetPropertyFunctionList(ctx, ctx->global_obj, js_base64_funcs, countof(js_base64_funcs));
57561+ }
57562+
57563+
5742557564/* CallSite */
5742657565
5742757566static void js_callsite_finalizer(JSRuntime *rt, JSValueConst val)
0 commit comments