add base64 and base64url encoder/decoders
This commit is contained in:
@@ -139,10 +139,165 @@ JSC_CCALL(text_base32_to_blob,
|
||||
return val;
|
||||
)
|
||||
|
||||
static int base64_char_to_val_standard(char c) {
|
||||
if (c >= 'A' && c <= 'Z') return c - 'A';
|
||||
if (c >= 'a' && c <= 'z') return c - 'a' + 26;
|
||||
if (c >= '0' && c <= '9') return c - '0' + 52;
|
||||
if (c == '+') return 62;
|
||||
if (c == '/') return 63;
|
||||
return -1;
|
||||
}
|
||||
static int base64_char_to_val_url(char c) {
|
||||
if (c >= 'A' && c <= 'Z') return c - 'A';
|
||||
if (c >= 'a' && c <= 'z') return c - 'a' + 26;
|
||||
if (c >= '0' && c <= '9') return c - '0' + 52;
|
||||
if (c == '-') return 62;
|
||||
if (c == '_') return 63;
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*─── blob → Base64 (standard, with ‘+’ and ‘/’, padded) ───────────────────*/
|
||||
JSC_CCALL(text_blob_to_base64,
|
||||
size_t blob_len;
|
||||
void *blob_data = js_get_blob_data(js, &blob_len, argv[0]);
|
||||
if (!blob_data) return JS_ThrowTypeError(js, "Expected stone blob");
|
||||
const uint8_t *bytes = blob_data;
|
||||
static const char b64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789+/";
|
||||
size_t out_len = ((blob_len + 2) / 3) * 4;
|
||||
char *out = malloc(out_len + 1);
|
||||
if (!out) return JS_ThrowOutOfMemory(js);
|
||||
|
||||
size_t in_i = 0, out_i = 0;
|
||||
while (in_i < blob_len) {
|
||||
uint32_t buf = 0;
|
||||
int to_read = (blob_len - in_i < 3 ? blob_len - in_i : 3);
|
||||
for (int j = 0; j < to_read; ++j) {
|
||||
buf = (buf << 8) | bytes[in_i++];
|
||||
}
|
||||
buf <<= 8 * (3 - to_read);
|
||||
out[out_i++] = b64[(buf >> 18) & 0x3F];
|
||||
out[out_i++] = b64[(buf >> 12) & 0x3F];
|
||||
out[out_i++] = (to_read > 1 ? b64[(buf >> 6) & 0x3F] : '=');
|
||||
out[out_i++] = (to_read > 2 ? b64[ buf & 0x3F] : '=');
|
||||
}
|
||||
out[out_len] = '\0';
|
||||
JSValue v = JS_NewString(js, out);
|
||||
free(out);
|
||||
return v;
|
||||
)
|
||||
|
||||
/*─── Base64 → blob (standard, expects ‘+’ and ‘/’, pads allowed) ────────────*/
|
||||
JSC_CCALL(text_base64_to_blob,
|
||||
const char *str = JS_ToCString(js, argv[0]);
|
||||
if (!str) return JS_ThrowTypeError(js, "Expected string");
|
||||
size_t len = strlen(str);
|
||||
// strip padding for length calculation
|
||||
size_t eff = len;
|
||||
while (eff > 0 && str[eff-1] == '=') eff--;
|
||||
size_t out_len = (eff * 6) / 8;
|
||||
uint8_t *out = malloc(out_len);
|
||||
if (!out) { JS_FreeCString(js, str); return JS_ThrowOutOfMemory(js); }
|
||||
|
||||
size_t in_i = 0, out_i = 0;
|
||||
while (in_i < eff) {
|
||||
uint32_t buf = 0;
|
||||
int to_read = (eff - in_i < 4 ? eff - in_i : 4);
|
||||
for (int j = 0; j < to_read; ++j) {
|
||||
int v = base64_char_to_val_standard(str[in_i++]);
|
||||
if (v < 0) { free(out); JS_FreeCString(js, str);
|
||||
return JS_ThrowTypeError(js, "Invalid base64 character"); }
|
||||
buf = (buf << 6) | v;
|
||||
}
|
||||
buf <<= 6 * (4 - to_read);
|
||||
int bytes_out = (to_read * 6) / 8;
|
||||
for (int j = 0; j < bytes_out && out_i < out_len; ++j) {
|
||||
out[out_i++] = (buf >> (16 - 8*j)) & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
JSValue v = js_new_blob_stoned_copy(js, out, out_len);
|
||||
free(out);
|
||||
JS_FreeCString(js, str);
|
||||
return v;
|
||||
)
|
||||
|
||||
/*─── blob → Base64URL (no padding, ‘-’ and ‘_’) ─────────────────────────────*/
|
||||
JSC_CCALL(text_blob_to_base64url,
|
||||
size_t blob_len;
|
||||
void *blob_data = js_get_blob_data(js, &blob_len, argv[0]);
|
||||
if (!blob_data) return JS_ThrowTypeError(js, "Expected stone blob");
|
||||
const uint8_t *bytes = blob_data;
|
||||
static const char b64url[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
"abcdefghijklmnopqrstuvwxyz"
|
||||
"0123456789-_";
|
||||
size_t raw_len = ((blob_len + 2) / 3) * 4;
|
||||
// we’ll drop any trailing '='
|
||||
char *out = malloc(raw_len + 1);
|
||||
if (!out) return JS_ThrowOutOfMemory(js);
|
||||
|
||||
size_t in_i = 0, out_i = 0;
|
||||
while (in_i < blob_len) {
|
||||
uint32_t buf = 0;
|
||||
int to_read = (blob_len - in_i < 3 ? blob_len - in_i : 3);
|
||||
for (int j = 0; j < to_read; ++j) {
|
||||
buf = (buf << 8) | bytes[in_i++];
|
||||
}
|
||||
buf <<= 8 * (3 - to_read);
|
||||
out[out_i++] = b64url[(buf >> 18) & 0x3F];
|
||||
out[out_i++] = b64url[(buf >> 12) & 0x3F];
|
||||
if (to_read > 1) out[out_i++] = b64url[(buf >> 6) & 0x3F];
|
||||
if (to_read > 2) out[out_i++] = b64url[ buf & 0x3F];
|
||||
}
|
||||
out[out_i] = '\0';
|
||||
JSValue v = JS_NewString(js, out);
|
||||
free(out);
|
||||
return v;
|
||||
)
|
||||
|
||||
/*─── Base64URL → blob (accepts ‘-’ / ‘_’, no padding needed) ─────────────────*/
|
||||
JSC_CCALL(text_base64url_to_blob,
|
||||
const char *str = JS_ToCString(js, argv[0]);
|
||||
if (!str) return JS_ThrowTypeError(js, "Expected string");
|
||||
size_t len = strlen(str);
|
||||
size_t eff = len; // no '=' in URL‐safe, but strip if present
|
||||
while (eff > 0 && str[eff-1] == '=') eff--;
|
||||
size_t out_len = (eff * 6) / 8;
|
||||
uint8_t *out = malloc(out_len);
|
||||
if (!out) { JS_FreeCString(js, str); return JS_ThrowOutOfMemory(js); }
|
||||
|
||||
size_t in_i = 0, out_i = 0;
|
||||
while (in_i < eff) {
|
||||
uint32_t buf = 0;
|
||||
int to_read = (eff - in_i < 4 ? eff - in_i : 4);
|
||||
for (int j = 0; j < to_read; ++j) {
|
||||
int v = base64_char_to_val_url(str[in_i++]);
|
||||
if (v < 0) { free(out); JS_FreeCString(js, str);
|
||||
return JS_ThrowTypeError(js, "Invalid base64url character"); }
|
||||
buf = (buf << 6) | v;
|
||||
}
|
||||
buf <<= 6 * (4 - to_read);
|
||||
int bytes_out = (to_read * 6) / 8;
|
||||
for (int j = 0; j < bytes_out && out_i < out_len; ++j) {
|
||||
out[out_i++] = (buf >> (16 - 8*j)) & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
JSValue v = js_new_blob_stoned_copy(js, out, out_len);
|
||||
free(out);
|
||||
JS_FreeCString(js, str);
|
||||
return v;
|
||||
)
|
||||
|
||||
static const JSCFunctionListEntry js_text_funcs[] = {
|
||||
MIST_FUNC_DEF(text, blob_to_hex, 1),
|
||||
MIST_FUNC_DEF(text, blob_to_base32, 1),
|
||||
MIST_FUNC_DEF(text, base32_to_blob, 1),
|
||||
MIST_FUNC_DEF(text, blob_to_base64, 1),
|
||||
MIST_FUNC_DEF(text, base64_to_blob, 1),
|
||||
MIST_FUNC_DEF(text, blob_to_base64url, 1),
|
||||
MIST_FUNC_DEF(text, base64url_to_blob, 1),
|
||||
};
|
||||
|
||||
JSValue js_text_use(JSContext *js)
|
||||
|
||||
Reference in New Issue
Block a user