apple1js/js/base64.ts

173 lines
4.5 KiB
TypeScript
Raw Normal View History

2023-12-18 20:04:20 +00:00
import {memory} from '@whscullin/cpu6502';
2023-08-06 02:08:59 +00:00
2023-08-06 14:32:11 +00:00
const B64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
2023-08-06 02:08:59 +00:00
/** Encode an array of bytes in base64. */
export function base64_encode(data: null | undefined): undefined;
export function base64_encode(data: memory): string;
export function base64_encode(
2023-08-19 22:07:00 +00:00
data: memory | null | undefined,
2023-08-06 02:08:59 +00:00
): string | undefined {
// Twacked by Will Scullin to handle arrays of 'bytes'
// http://kevin.vanzonneveld.net
// + original by: Tyler Akins (http://rumkin.com)
// + improved by: Bayron Guevara
// + improved by: Thunder.m
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + bugfixed by: Pellentesque Malesuada
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// - depends on: utf8_encode
// * example 1: base64_encode('Kevin van Zonneveld');
// * returns 1: 'S2V2aW4gdmFuIFpvbm5ldmVsZA=='
// mozilla has this native
// - but breaks in 2.0.0.12!
//if (typeof this.window['atob'] == 'function') {
// return atob(data);
//}
let o1,
o2,
o3,
h1,
h2,
h3,
h4,
bits,
i = 0,
ac = 0,
2023-08-06 14:32:11 +00:00
enc = '';
2023-08-06 02:08:59 +00:00
const tmp_arr = [];
if (!data) {
return undefined;
}
do {
// pack three octets into four hexets
o1 = data[i++];
o2 = data[i++];
o3 = data[i++];
bits = (o1 << 16) | (o2 << 8) | o3;
h1 = (bits >> 18) & 0x3f;
h2 = (bits >> 12) & 0x3f;
h3 = (bits >> 6) & 0x3f;
h4 = bits & 0x3f;
// use hexets to index into b64, and append result to encoded string
tmp_arr[ac++] =
B64.charAt(h1) + B64.charAt(h2) + B64.charAt(h3) + B64.charAt(h4);
} while (i < data.length);
2023-08-06 14:32:11 +00:00
enc = tmp_arr.join('');
2023-08-06 02:08:59 +00:00
switch (data.length % 3) {
case 1:
2023-08-06 14:32:11 +00:00
enc = enc.slice(0, -2) + '==';
2023-08-06 02:08:59 +00:00
break;
case 2:
2023-08-06 14:32:11 +00:00
enc = enc.slice(0, -1) + '=';
2023-08-06 02:08:59 +00:00
break;
}
return enc;
}
/** Returns undefined if the input is null or undefined. */
export function base64_decode(data: null | undefined): undefined;
/** Returns an array of bytes from the given base64-encoded string. */
export function base64_decode(data: string): memory;
/** Returns an array of bytes from the given base64-encoded string. */
export function base64_decode(
2023-08-19 22:07:00 +00:00
data: string | null | undefined,
2023-08-06 02:08:59 +00:00
): memory | undefined {
// Twacked by Will Scullin to handle arrays of 'bytes'
// http://kevin.vanzonneveld.net
// + original by: Tyler Akins (http://rumkin.com)
// + improved by: Thunder.m
// + input by: Aman Gupta
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + bugfixed by: Onno Marsman
// + bugfixed by: Pellentesque Malesuada
// + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// + input by: Brett Zamir (http://brett-zamir.me)
// + bugfixed by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
// - depends on: utf8_decode
// * example 1: base64_decode('S2V2aW4gdmFuIFpvbm5ldmVsZA==');
// * returns 1: 'Kevin van Zonneveld'
// mozilla has this native
// - but breaks in 2.0.0.12!
//if (typeof this.window['btoa'] == 'function') {
// return btoa(data);
//}
let o1,
o2,
o3,
h1,
h2,
h3,
h4,
bits,
i = 0,
ac = 0;
const tmp_arr = [];
if (!data) {
return undefined;
}
do {
// unpack four hexets into three octets using index points in B64
h1 = B64.indexOf(data.charAt(i++));
h2 = B64.indexOf(data.charAt(i++));
h3 = B64.indexOf(data.charAt(i++));
h4 = B64.indexOf(data.charAt(i++));
bits = (h1 << 18) | (h2 << 12) | (h3 << 6) | h4;
o1 = (bits >> 16) & 0xff;
o2 = (bits >> 8) & 0xff;
o3 = bits & 0xff;
tmp_arr[ac++] = o1;
if (h3 !== 64) {
tmp_arr[ac++] = o2;
}
if (h4 !== 64) {
tmp_arr[ac++] = o3;
}
} while (i < data.length);
return new Uint8Array(tmp_arr);
}
2023-08-06 14:32:11 +00:00
const DATA_URL_PREFIX = 'data:application/octet-stream;base64,';
2023-08-06 02:08:59 +00:00
export function base64_json_parse(json: string): unknown {
const reviver = (_key: string, value: unknown) => {
2023-08-06 14:32:11 +00:00
if (typeof value === 'string' && value.startsWith(DATA_URL_PREFIX)) {
2023-08-06 02:08:59 +00:00
return base64_decode(value.slice(DATA_URL_PREFIX.length));
}
return value;
};
return JSON.parse(json, reviver);
}
export function base64_json_stringify(json: unknown) {
const replacer = (_key: string, value: unknown) => {
if (value instanceof Uint8Array) {
return DATA_URL_PREFIX + base64_encode(value);
}
return value;
};
return JSON.stringify(json, replacer);
}