61 lines
2.3 KiB
TypeScript
61 lines
2.3 KiB
TypeScript
/** Compares two byte sequences without coercion or serialization. */
|
|
export function bytesEqual(left: Uint8Array, right: Uint8Array): boolean {
|
|
if (left.byteLength !== right.byteLength) return false;
|
|
|
|
for (let index = 0; index < left.byteLength; index += 1) {
|
|
if (left[index] !== right[index]) return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/** Returns an owned copy so callers cannot mutate adapter-held bytes. */
|
|
export function copyBytes(value: Uint8Array): Uint8Array {
|
|
return value.slice();
|
|
}
|
|
|
|
const BASE64_ALPHABET =
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
|
|
|
/** Encodes arbitrary bytes using RFC 4648 standard base64 with padding. */
|
|
export function bytesToBase64(value: Uint8Array): string {
|
|
let result = "";
|
|
|
|
for (let index = 0; index < value.length; index += 3) {
|
|
const first = value[index] ?? 0;
|
|
const second = value[index + 1] ?? 0;
|
|
const third = value[index + 2] ?? 0;
|
|
const packed = (first << 16) | (second << 8) | third;
|
|
result += BASE64_ALPHABET[(packed >>> 18) & 63];
|
|
result += BASE64_ALPHABET[(packed >>> 12) & 63];
|
|
result += index + 1 < value.length ? BASE64_ALPHABET[(packed >>> 6) & 63] : "=";
|
|
result += index + 2 < value.length ? BASE64_ALPHABET[packed & 63] : "=";
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/** Decodes strict, padded RFC 4648 base64 and rejects malformed input. */
|
|
export function base64ToBytes(value: string): Uint8Array {
|
|
if (value.length % 4 !== 0 || !/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/.test(value)) {
|
|
throw new TypeError("Invalid base64 value");
|
|
}
|
|
|
|
const padding = value.endsWith("==") ? 2 : value.endsWith("=") ? 1 : 0;
|
|
const result = new Uint8Array((value.length / 4) * 3 - padding);
|
|
let output = 0;
|
|
|
|
for (let index = 0; index < value.length; index += 4) {
|
|
const a = BASE64_ALPHABET.indexOf(value[index] ?? "");
|
|
const b = BASE64_ALPHABET.indexOf(value[index + 1] ?? "");
|
|
const c = value[index + 2] === "=" ? 0 : BASE64_ALPHABET.indexOf(value[index + 2] ?? "");
|
|
const d = value[index + 3] === "=" ? 0 : BASE64_ALPHABET.indexOf(value[index + 3] ?? "");
|
|
const packed = (a << 18) | (b << 12) | (c << 6) | d;
|
|
if (output < result.length) result[output++] = (packed >>> 16) & 255;
|
|
if (output < result.length) result[output++] = (packed >>> 8) & 255;
|
|
if (output < result.length) result[output++] = packed & 255;
|
|
}
|
|
|
|
return result;
|
|
}
|