Files
storage-v1/src/shared/bytes.ts

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;
}