import { ArrayPrototypeJoin as join, ArrayPrototypePush as push, String, StringPrototypeSlice as slice, uncurryThis, } from "ext:core/primordials"; import { createException } from "./events.js"; import { array, required, string } from "./idl.js"; const alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; const charCode = uncurryThis(String.prototype.charCodeAt); const fromCharCode = String.fromCharCode; function invalidCharacter(): Error { return createException({ message: "The string contains characters outside the Latin1 range.", name: "InvalidCharacterError", }) as Error; } function sextet(code: number): number { if (code >= 65 && code <= 90) return code - 65; if (code >= 97 && code <= 122) return code - 71; if (code >= 48 && code <= 57) return code + 4; if (code === 43) return 62; if (code === 47) return 63; return -1; } const api = { btoa(data: unknown): string { required(arguments.length, 1); data = string(data); const output = array(); let chunk = ""; for (let index = 0; index < (data as string).length; index += 3) { const a = charCode(data as string, index); const b = index + 1 < (data as string).length ? charCode(data as string, index + 1) : -1; const c = index + 2 < (data as string).length ? charCode(data as string, index + 2) : -1; if (a > 255 || b > 255 || c > 255) throw invalidCharacter(); chunk += alphabet[a >> 2]; chunk += alphabet[((a & 3) << 4) | (b < 0 ? 0 : b >> 4)]; chunk += b < 0 ? "==" : c < 0 ? alphabet[(b & 15) << 2] + "=" : alphabet[((b & 15) << 2) | (c >> 6)] + alphabet[c & 63]; if (chunk.length >= 4096) { push(output, chunk); chunk = ""; } } if (chunk) push(output, chunk); return join(output, ""); }, atob(data: unknown): string { required(arguments.length, 1); data = string(data); const inputParts = array(); for (let index = 0; index < (data as string).length; index++) { const code = charCode(data as string, index); if ( code === 9 || code === 10 || code === 12 || code === 13 || code === 32 ) { continue; } push(inputParts, (data as string)[index]); } let input = join(inputParts, ""); const length = input.length; if ( length % 4 === 0 && length >= 2 && charCode(input, length - 1) === 61 && charCode(input, length - 2) === 61 ) { input = slice(input, 0, length - 2); } else if (length % 4 === 0 && charCode(input, length - 1) === 61) { input = slice(input, 0, length - 1); } const remainder = input.length % 4; if (remainder === 1) throw invalidCharacter(); const values = array(); for (let index = 0; index < input.length; index++) { const value = sextet(charCode(input, index)); if (value < 0) throw invalidCharacter(); values[index] = value; } const output = array(); let chunk = ""; let index = 0; for (; index + 3 < values.length; index += 4) { chunk += fromCharCode( (values[index] << 2) | (values[index + 1] >> 4), ((values[index + 1] & 15) << 4) | (values[index + 2] >> 2), ((values[index + 2] & 3) << 6) | values[index + 3], ); if (chunk.length >= 4096) { push(output, chunk); chunk = ""; } } if (remainder === 2) { chunk += fromCharCode((values[index] << 2) | (values[index + 1] >> 4)); } else if (remainder === 3) { chunk += fromCharCode( (values[index] << 2) | (values[index + 1] >> 4), ((values[index + 1] & 15) << 4) | (values[index + 2] >> 2), ); } if (chunk) push(output, chunk); return join(output, ""); }, }; const { atob, btoa } = api; export { atob, btoa };