Skip to content

Instantly share code, notes, and snippets.

@JTRNS
Created April 13, 2024 07:27
Show Gist options
  • Save JTRNS/ed7c6a8d800e5bedc13d2b5644e60326 to your computer and use it in GitHub Desktop.
Save JTRNS/ed7c6a8d800e5bedc13d2b5644e60326 to your computer and use it in GitHub Desktop.
base64 encoder and decoder
function base64ToBytes(base64: string): Uint8Array {
const binString = atob(base64);
return Uint8Array.from(binString, (m) => m.codePointAt(0) || 0);
}
function bytesToBase64(bytes: Uint8Array): string {
const binString = String.fromCodePoint(...bytes);
return btoa(binString);
}
function ensureWellFormed(str: string | Uint8Array): void {
if (typeof str === "string" && !isWellFormed(str)) {
throw new Error("Cannot process a string with lone surrogates");
}
}
function isWellFormed(str: string) {
/// @ts-expect-error Missing type definition
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/isWellFormed
if (typeof (str.isWellFormed) != "undefined") {
/// @ts-expect-error Missing type definition
return str.isWellFormed();
} else {
try {
encodeURIComponent(str);
return true;
} catch (error) {
return false;
}
}
}
const encoder = new TextEncoder();
export function validateBinaryLike(source: unknown): Uint8Array {
if (typeof source === "string") {
return encoder.encode(source);
} else if (source instanceof Uint8Array) {
return source;
} else if (source instanceof ArrayBuffer) {
return new Uint8Array(source);
}
throw new TypeError(
`The input must be a Uint8Array, a string, or an ArrayBuffer. Received a value of the type ${typeof source}
}.`,
);
}
export const base64 = {
encode(binaryData: Uint8Array | ArrayBuffer | string): string {
const bytes = validateBinaryLike(binaryData);
ensureWellFormed(bytes);
return bytesToBase64(bytes);
},
decode(value: string): Uint8Array {
ensureWellFormed(value);
return base64ToBytes(value);
},
};
export const base64url = {
encode(binaryData: Uint8Array | ArrayBuffer | string): string {
const bytes = validateBinaryLike(binaryData);
ensureWellFormed(bytes);
return bytesToBase64(bytes)
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=/g, "");
},
decode(value: string): Uint8Array {
ensureWellFormed(value);
return base64ToBytes(
value
.replace(/-/g, "+")
.replace(/_/g, "/")
.padEnd((value.length + 3) & ~3, "="),
);
},
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment